pipefy-api-fallback — Claude Code skill: install
This skill falls back to direct Pipefy GraphQL calls when toolkit operations and schema introspection fail. Use it for API troubleshooting and recovery.
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a claude-codeWhat pipefy-api-fallback does
pipefy-api-fallback sends Pipefy GraphQL queries directly with curl or httpx. It covers queries, mutations, and schema discovery when the toolkit connection is unavailable or GraphQL fails at the infrastructure level. The skill follows the required order: try a dedicated operation first, then introspection with execute_graphql, and use raw HTTP last.
Authentication can use a Service Account with OAuth2 or a Personal Access Token stored in an environment variable. The Service Account takes precedence when both are available. The skill maps errors such as PERMISSION_DENIED, invalid_input, and undefinedField, and re-reads an object after an ambiguous write before considering another attempt. Raw HTTP is a Tier 3 fallback, so a single failed operation is not enough to trigger it.
What it helps with
- send a direct GraphQL request to Pipefy
- inspect the public GraphQL schema
- diagnose authentication or permission errors
- re-read a resource after an ambiguous mutation
Example prompts for the agent
- Use
pipefy-api-fallbackafter a toolkit operation and introspection both fail. - Inspect the Pipefy schema through a direct GraphQL request and explain the error.
- Re-read the target after the write result is unclear before attempting anything again.
How to install pipefy-api-fallback
1Skills CLI (Vercel)
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a claude-code2Manually: put the files in place
~/.claude/skills/pipefy-api-fallback/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/pipefy-api-fallback/
1Skills CLI (Vercel)
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a codex2Manually: put the files in place
.agents/skills/pipefy-api-fallback/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a cursor2Manually: put the files in place
.cursor/skills/pipefy-api-fallback/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install pipefy/ai-toolkit pipefy-api-fallback2Manually: put the files in place
.github/skills/pipefy-api-fallback/SKILL.md1Skills CLI (Vercel)
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a gemini-cli2Platform CLI
gemini skills install https://github.com/pipefy/ai-toolkit --path plugins/pipefy/skills/pipefy-api-fallbackManually: put the files in place
.devin/skills/pipefy-api-fallback/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about pipefy-api-fallback
How do I install pipefy-api-fallback in Claude Code?
Run npx skills add pipefy/ai-toolkit --skill pipefy-api-fallback -a claude-code, then check ~/.claude/skills/pipefy-api-fallback/SKILL.md.
What does pipefy-api-fallback do?
It calls the Pipefy GraphQL API directly with curl or httpx after the first two resolution tiers fail. It can also inspect the schema and interpret API errors.
Which authentication methods does the skill use?
It supports a Service Account through OAuth2 or a Personal Access Token held in an environment variable. The Service Account is preferred when both options are present, and the user must be told where the variable value will be sent before it is read.
Source text
SKILL.md · Apache-2.0
---
name: pipefy-api-fallback
description: >
Use this skill when a toolkit operation fails AND the introspection skill could
not resolve the problem. This is the last-resort fallback (Tier 3):
call the Pipefy GraphQL API directly using curl or httpx, authenticating
with the Service Account (OAuth2) or a Personal Access Token (PAT)
available as env var. Follow the 3-tier resolution strategy before
reaching this point.
tags: [pipefy, graphql, api, fallback, troubleshooting, curl]
---
# Pipefy API Fallback (Tier 3 — Last Resort)
Read the [MCP reference](references/mcp.md) or [CLI reference](references/cli.md) for the surface you are using. Load only the relevant reference.
This skill activates only after Tiers 1 and 2 have failed. Call the Pipefy GraphQL API directly, bypassing the toolkit connection.
If a write reports failure, re-read the target before changing tiers or retrying. The mutation may already have applied; follow [Ambiguous write failure](#ambiguous-write-failure-re-read-before-retry).
---
## 3-tier resolution strategy (always follow in order)
1. Try a dedicated operation (`create_card`, `get_phase_cards`, `get_phase_allowed_move_targets`, `update_pipe`, etc.). For card/phase seeding and inventory, see `pipefy-pipes-and-cards` — Seed pipe across phases.
2. Use schema introspection and `execute_graphql` when a dedicated operation is unavailable or fails unexpectedly. See `pipefy-introspection`.
3. Use direct HTTP only when the toolkit connection is unavailable or GraphQL execution fails with an infrastructure error.
**Do not jump to Tier 3 after a single failure.** Follow the tiers in order. Testing a new mutation belongs in Tier 2.
---
## Authentication
Two options (use whichever is available in the environment). Prefer the Service Account when both exist.
To see which variables are set, list their names only, never their values:
```bash
env | cut -d= -f1 | grep '^PIPEFY_'
```
Do not run `env`, `printenv`, or `echo` on these variables to choose an option: that prints the secret into the conversation before the user agrees.
Before reading any `PIPEFY_*` variable, tell the user which variable you will read and that its value goes to `app.pipefy.com` in the `Authorization` header or the token request, and wait for a yes. Do not read it from a file the user did not name.
**Option A — OAuth2 Client Credentials (preferred):**
```bash
TOKEN=$(curl -s -X POST https://app.pipefy.com/oauth/token \
-H "Content-Type: application/json" \
-d "{\"grant_type\":\"client_credentials\",\"client_id\":\"$PIPEFY_SERVICE_ACCOUNT_CLIENT_ID\",\"client_secret\":\"$PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
```
**Option B — Personal Access Token (PAT):**
```bash
TOKEN="$PIPEFY_PAT" # or $PIPEFY_TOKEN
```
PATs are deprecated for new integrations but may still exist in the environment.
### Token rules
- The `Bearer ` prefix is **mandatory** — Pipefy rejects requests without it.
- Never expose `PIPEFY_SERVICE_ACCOUNT_CLIENT_ID`, `PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET`, `PIPEFY_PAT`, or `PIPEFY_TOKEN` in responses to the user or in logs.
- Service Account tokens are reused while valid; only re-fetch on expiry (401).
---
## Endpoints
| Purpose | URL |
|---------|-----|
| Queries, mutations, and introspection | `https://app.pipefy.com/graphql` |
| OAuth2 token | `https://app.pipefy.com/oauth/token` |
This one endpoint serves queries, mutations, and introspection of the public schema, and `https://api.pipefy.com/graphql` answers the same way. It is the endpoint the toolkit uses for the public schema (`PIPEFY_BASE_URL` plus `/graphql`). Portal tools and a few others use two more endpoints, `/graphql/interfaces` and `/internal_api`; see [`docs/mcp/tools/portal.md`](https://github.com/pipefy/ai-toolkit/blob/main/docs/mcp/tools/portal.md).
---
## Execute a GraphQL query
```bash
curl -s -X POST https://app.pipefy.com/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "{ me { id name } }"}' | jq .
```
## Execute a GraphQL mutation
```bash
curl -s -X POST https://app.pipefy.com/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateCard($input: CreateCardInput!) { createCard(input: $input) { card { id } } }",
"variables": {
"input": {
"pipe_id": 67890,
"title": "Fallback Card"
}
}
}' | jq .
```
---
## Introspection via raw API
When you need to discover schema over direct HTTP, call the same endpoint:
```bash
# All queries and mutations
curl -s -X POST https://app.pipefy.com/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { queryType { fields { name description } } mutationType { fields { name description } } } }"}'
# Type details
curl -s -X POST https://app.pipefy.com/graphql \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ __type(name: \"CreateCardInput\") { inputFields { name description type { name kind ofType { name kind } } } } }"}'
```
---
## Error code → cause
GraphQL errors, including syntax errors (`GRAPHQL_PARSE_FAILED`), return HTTP 200: check the `errors` array and its `extensions.code`, not the HTTP status code. An authentication failure returns HTTP 401, and a request body that is not valid JSON returns HTTP 500 with a plain-text page.
| Code | Likely cause | Recovery |
|-------|--------------|----------|
| HTTP 401, body `{"errors": [{"title": "Unauthorized", ...}]}` | `Authorization` header missing or empty, or the token sent without the `Bearer ` prefix | Fix the header: `Authorization: Bearer $TOKEN`, with a non-empty token. |
| HTTP 401, body `{"error": "invalid_token", ...}` | Bearer token present but invalid or expired | Re-fetch the token (Option A). |
| PERMISSION_DENIED | Service Account not a member of this pipe/table; Pipefy also answers this for a pipe or card ID that does not exist | Verify the ID; add the SA via `invite_members` or ask the user. |
| resource_not_found | ID does not exist or SA cannot see it | Verify ID; check pipe/table membership. |
| invalid_input | Wrong argument name or type | Run `introspect_type` (Tier 2) to recheck the input shape. |
| undefinedField | The query selects a field the type does not have | Run `introspect_type` on the type and fix the selection. |
| INTERNAL_SERVER_ERROR | API bug or unsupported payload | **Do NOT retry the same payload.** Try an alternative mutation or workaround. |
| missingRequiredInputObjectAttribute | A required field is missing from the input | Compare the payload against `__type(name: …InputType)`. |
| Ambiguous write failure (`success: false`, empty or unclear message) | Mutation may already have applied (side effects on the server) | **Re-read before any retry** — see [Ambiguous write failure](#ambiguous-write-failure-re-read-before-retry). |
---
## Ambiguous write failure (re-read before retry)
Write operations and `execute_graphql` can report failure even when the mutation already applied. Blind retry duplicates customer data (e.g. many cards created despite `success: false`).
1. **Do not** immediately re-run the same create/mutation.
2. **Re-read** first: `get_cards` / `get_phase_cards_count` / returned ids / `cards_count` on the pipe or phase you targeted. Prefer comparing to a count or id set you recorded **before** the write when available.
3. Retry **only** if the re-read clearly shows the write did not land.
4. If the re-read is **inconclusive** (no pre-write baseline, pagination truncates results, or counts cannot prove absence), **stop** — report the ambiguous failure and the re-read evidence to the user. Do not guess-retry.
5. **Hard stop — no client-side idempotency:** `CreateCardInput` has no `idempotency_key` (only `clientMutationId`, which is not create-idempotency). Do not invent client-side keys or assume retries are safe. Re-read is the only safe path until the API adds real idempotency.
---
## Known workarounds
### Cross-pipe card creation
- `createAutomation` with `action: create_card` + `field_map` can return `INTERNAL_SERVER_ERROR` for a cross-pipe target. Do not retry that payload unchanged.
- For a **one-time** connected card, `createCard` with `throughConnectors` can create the card when a connector field has `canCreateNewConnected: true`.
- `createCard` does not create an automation rule. If the user needs future cards created automatically, report that the one-time call does not meet that requirement.
### Pipe listing shorter than `pipesCount`
- `pipesCount` is the org-wide total; `organization { pipes { ... } }` and `search_pipes` return only the pipes the calling identity is a member of. A shorter listing, or an empty one, is expected behavior and not an error. Role does not widen it: a `super_admin` gets the same membership-scoped result. Detail and workarounds: [`docs/mcp/tools/organization.md`](https://github.com/pipefy/ai-toolkit/blob/main/docs/mcp/tools/organization.md#why-counts-disagree).
- `organization { pipes(include_publics: true) }` widens the listing with pipes that are public inside the org. It still normally returns fewer than `pipesCount`.
- Service accounts hit this most often: an SA starts as a member of nothing.
- Pipes created via API are automatically visible to the SA.
- Pipes created in the UI require the SA to be added as an admin.
- Workaround: get pipe IDs from the user once and query `pipe(id: "...")` directly.
### `invite_members` accepts unknown emails silently
- Pipefy mints a new `user_id` for typo addresses without rejecting the invite. Sanity-check email syntax before calling.
---
## External resources (when raw API also fails)
- Pipefy developer portal:
- https://developers.pipefy.com/reference/cards
- https://developers.pipefy.com/reference/pipes
- https://developers.pipefy.com/reference/automation-creation
- https://developers.pipefy.com/reference/how-to-handle-errors
- API reference:
- https://api-docs.pipefy.com/reference/mutations/overview/
- https://api-docs.pipefy.com/reference/queries/overview/
- Community + changelog:
- https://community.pipefy.com/api-76
- https://developers.pipefy.com/changelog
- Status page: https://status.pipefy.com
Search for the exact error message + "Pipefy GraphQL", or the mutation name + "example Pipefy API".
---
## Escalation to the user (absolute last resort)
Only after all 3 tiers and external resources have failed:
1. State exactly what was tried (dedicated operation, introspection, raw API).
2. Show the verbatim error response.
3. Propose a concrete workaround (e.g., "create via the Pipefy UI, then continue via API with the resulting ID").
4. Stop — do not loop.
---
## Success criteria
- The operation completes without an HTTP 4xx/5xx error.
- The response contains a `data` key and `errors` is null or absent.
## Failure modes
- **HTTP 401**: header missing, empty, or without `Bearer ` (body `Unauthorized`), or the token is invalid or expired (body `invalid_token`). Fix the header, or re-fetch the OAuth token (Option A).
- **`GRAPHQL_PARSE_FAILED` at HTTP 200**: GraphQL syntax error. Validate the query string and escape quotes properly when embedding via shell.
- **HTTP 500**: first check that the request body is valid JSON, because a malformed body also returns 500. If it is, treat it as a Pipefy API outage: check [status.pipefy.com](https://status.pipefy.com) and retry later. Do not loop.
- **`INTERNAL_SERVER_ERROR` in `errors` array** — do NOT retry the same payload; pick a different mutation path.
- **Ambiguous write failure** — reported error with empty/unclear message after a create or other write: re-read counts/ids before retrying; never blind-retry creates ([Ambiguous write failure](#ambiguous-write-failure-re-read-before-retry)).
## Security notes
- Never log or print tokens in plain text.
- Prefer environment variables over inline credentials.
- Use `PIPEFY_TOKEN` / `PIPEFY_PAT` only for personal/development use; use service-account credentials (`PIPEFY_SERVICE_ACCOUNT_CLIENT_ID` + `PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET`) for service accounts.
## See also
- `pipefy-introspection` — Tier 2: use `execute_graphql` and introspection tools before falling back to direct HTTP.
© pipefy · Apache-2.0 · pipefy/ai-toolkit