.agents/skills/piece-builder/SKILL.md
Build pieces (integrations) for the Activepieces automation platform. Each piece provides actions (operations users can perform) and triggers (events that start flows).
Follow these 5 steps every time:
packages/pieces/custom/ (no need to ask)packages/pieces/community/npm run cli pieces create
# Enter: piece name, package name (@activepieces/piece-<name>), type (community)
npm run cli actions create
# Enter: piece folder name, action display name, description
npm run cli triggers create
# Enter: piece folder name, trigger display name, description, technique (polling/webhook)
Create this structure under packages/pieces/community/<name>/:
src/
index.ts
lib/
actions/ # One file per action
triggers/ # One file per trigger
common/ # Shared helpers (optional)
package.json
project.json
.eslintrc.json
tsconfig.json
tsconfig.lib.json
Copy config files from an existing simple piece (e.g. packages/pieces/core/qrcode/) and replace <name> throughout. Templates:
package.json
{
"name": "@activepieces/piece-<name>",
"version": "0.0.1",
"dependencies": {}
}
.eslintrc.json
{
"extends": ["../../../../.eslintrc.base.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {} },
{ "files": ["*.ts", "*.tsx"], "rules": {} },
{ "files": ["*.js", "*.jsx"], "rules": {} }
]
}
tsconfig.json
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [{ "path": "./tsconfig.lib.json" }]
}
tsconfig.lib.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"baseUrl": ".",
"paths": {},
"outDir": "./dist",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
}
Read these files before writing any code:
| What you're implementing | Read this file |
|---|---|
| Auth setup | auth-patterns.md |
| Action structure | action-patterns.md |
| Trigger structure (polling or webhook) | trigger-patterns.md |
| Any props -- actions or triggers | props-patterns.md |
| HTTP client, shared helpers, pagination | common-patterns.md |
| Every prop and dropdown (mandatory for all) | ux-guidelines.md |
| Every return value (mandatory for all) | output-quality.md |
ux-guidelines.md and output-quality.md apply to every action and trigger -- read them before starting, not only when unsure.
src/index.ts and add to actions: [...]src/index.ts and add to triggers: [...]src/index.ts so actions/triggers can import it via import { myAppAuth } from '../../'createCustomApiCallAction to actions: [...] for power userstsconfig.base.json at the repo root (insert alphabetically):
"@activepieces/piece-<name>": ["packages/pieces/community/<name>/src/index.ts"]
bun install
npx turbo run build --filter=@activepieces/piece-<name>
bun install is required for new pieces so the workspace symlinks are created before TypeScript can resolve imports. Skip it on subsequent rebuilds.
Fix TypeScript errors and rebuild. Common causes: missing import in src/index.ts, missing tsconfig.base.json entry, wrong type cast on context.auth (use context.auth as unknown as string for SecretText), missing sampleData on a trigger.
Add to packages/server/api/.env:
AP_DEV_PIECES=<name>
Start the dev server (npm start), open localhost:4200, sign in with [email protected] / 12345678, and find your piece in the flow builder.
| Location | Purpose |
|---|---|
packages/pieces/community/ | Third-party integrations (Slack, Stripe, etc.) -- use this for almost all work |
packages/pieces/core/ | Built-in platform utilities (HTTP, Store, Math, etc.) -- do NOT recreate these |
packages/pieces/custom/ | Private customer-specific pieces |
Full reference: piece-types.md -- includes all PieceCategory values and the list of existing core pieces.
packages/pieces/community/<piece-name>/
src/
index.ts # Piece definition (auth + imports + createPiece)
lib/
actions/ # One file per action
triggers/ # One file per trigger
common/ # Shared API helpers and dropdown definitions
package.json
project.json
.eslintrc.json
tsconfig.json
tsconfig.lib.json
| API Auth Method | Activepieces Type | Access Pattern |
|---|---|---|
| API key / Bearer token | PieceAuth.SecretText() | context.auth (string) |
| OAuth2 | PieceAuth.OAuth2() | context.auth.access_token |
| Username + password | PieceAuth.BasicAuth() | context.auth.username, .password |
| Multiple fields | PieceAuth.CustomAuth() | context.auth.<field_name> |
| No auth needed | PieceAuth.None() | No context.auth available |
Full code examples: read auth-patterns.md
import { createPiece, PieceAuth } from '@activepieces/pieces-framework';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { PieceCategory } from '@activepieces/shared';
import { myAction } from './lib/actions/my-action';
import { myTrigger } from './lib/triggers/my-trigger';
export const myAppAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: 'Go to Settings > API Keys in your dashboard to generate a key.',
required: true,
});
export const myApp = createPiece({
displayName: 'My App',
description: 'What the app does in one sentence.',
minimumSupportedRelease: '0.36.1',
// Logo: add a PNG to packages/pieces/community/<name>/ and reference it here.
logoUrl: 'https://cdn.activepieces.com/pieces/my-app.png',
categories: [PieceCategory.PRODUCTIVITY],
auth: myAppAuth,
authors: ['your-github-username'], // GitHub usernames of contributors
actions: [
myAction,
createCustomApiCallAction({
baseUrl: () => 'https://api.example.com/v1',
auth: myAppAuth,
authMapping: async (auth) => ({
Authorization: `Bearer ${auth}`,
}),
}),
],
triggers: [myTrigger],
// If the piece needs third-party npm packages, declare them here:
// npmDependencies: { 'some-sdk': '^1.0.0' },
});
Pieces are used by people who have never seen an API. Every prop, dropdown, and description must be clear enough to use without reading external docs.
Rules:
"Jane Doe ([email protected])" not "cus_abc123").Property.MarkDown() with numbered steps when a prop requires configuration in the third-party app."Create Contact" not "POST /contacts". Triggers: "New Order" not "order.created webhook"."Please select a project first" not just empty.Full patterns and examples: read ux-guidelines.md
Every action output must be directly mappable to Google Sheets, Excel, and Activepieces Tables. Users pipe piece outputs into spreadsheets constantly -- if your output is nested or inconsistent, it breaks their flows.
Rules:
{ user: { name: "Jo", email: "[email protected]" } } into { user_name: "Jo", user_email: "[email protected]" }.company_name not cName. These become column headers.Full patterns and examples: read output-quality.md
compilerOptions.paths. Build fails without this.name field in createAction/createTrigger must never change after publishing.import { myAppAuth } from '../../'.{}.Always pause and ask if: