원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.
Pipefy API Fallback (Tier 3 — Last Resort)
Read the MCP reference or CLI reference 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.
3-tier resolution strategy (always follow in order)
- Try a dedicated operation (
create_card,get_phase_cards,get_phase_allowed_move_targets,update_pipe, etc.). For card/phase seeding and inventory, seepipefy-pipes-and-cards— Seed pipe across phases. - Use schema introspection and
execute_graphqlwhen a dedicated operation is unavailable or fails unexpectedly. Seepipefy-introspection. - 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:
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):
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):
TOKEN="$PIPEFY_PAT" # or $PIPEFY_TOKENPATs are deprecated for new integrations but may still exist in the environment.
Token rules
- The
Bearerprefix is mandatory — Pipefy rejects requests without it. - Never expose
PIPEFY_SERVICE_ACCOUNT_CLIENT_ID,PIPEFY_SERVICE_ACCOUNT_CLIENT_SECRET,PIPEFY_PAT, orPIPEFY_TOKENin 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`.
Execute a GraphQL query
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
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:
# 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. |
| resourcenotfound | 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. |
| INTERNALSERVERERROR | 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)
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).
- Do not immediately re-run the same create/mutation.
- Re-read first:
get_cards/get_phase_cards_count/ returned ids /cards_counton the pipe or phase you targeted. Prefer comparing to a count or id set you recorded before the write when available. - Retry only if the re-read clearly shows the write did not land.
- 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.
- Hard stop — no client-side idempotency:
CreateCardInputhas noidempotency_key(onlyclientMutationId, 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
createAutomationwithaction: create_card+field_mapcan returnINTERNAL_SERVER_ERRORfor a cross-pipe target. Do not retry that payload unchanged.- For a one-time connected card,
createCardwiththroughConnectorscan create the card when a connector field hascanCreateNewConnected: true. createCarddoes 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
pipesCountis the org-wide total;organization { pipes { ... } }andsearch_pipesreturn 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: asuper_admingets the same membership-scoped result. Detail and workarounds: `docs/mcp/tools/organization.md`.organization { pipes(include_publics: true) }widens the listing with pipes that are public inside the org. It still normally returns fewer thanpipesCount.- 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_idfor 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:
- State exactly what was tried (dedicated operation, introspection, raw API).
- Show the verbatim error response.
- Propose a concrete workaround (e.g., "create via the Pipefy UI, then continue via API with the resulting ID").
- Stop — do not loop.
Success criteria
- The operation completes without an HTTP 4xx/5xx error.
- The response contains a
datakey anderrorsis null or absent.
Failure modes
- HTTP 401: header missing, empty, or without
Bearer(bodyUnauthorized), or the token is invalid or expired (bodyinvalid_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 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).
Security notes
- Never log or print tokens in plain text.
- Prefer environment variables over inline credentials.
- Use
PIPEFY_TOKEN/PIPEFY_PATonly 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: useexecute_graphqland introspection tools before falling back to direct HTTP.

