Back to Activepieces

Activepieces Piece Builder

.agents/skills/piece-builder/SKILL.md

0.85.412.9 KB
Original Source

Activepieces Piece Builder

Pick your task mode first

ModeWhat you're doingWhere to go
New pieceBuilding an integration for an app that has no piece yetFull 5-step workflow below
Add action / triggerAn existing piece needs another operation or eventSkip Steps 1–3. Open the existing piece, match its conventions (its common/ helpers, auth access, file naming, error handling), then jump to Step 4 IMPLEMENT and Step 5 WIRE & VERIFY. Bump the piece version.
Fix a bugAn existing action/trigger misbehavesReproduce → read the offending file and its common/ helpers → smallest fix that matches surrounding style → Step 5 VERIFY. Bump the piece version.

Golden rule for existing-piece modes: the piece you're editing is the source of truth, not these templates. If the piece already has a helper, a particular auth access pattern, or a way of shaping output, follow that. Reach into the reference files only for a pattern the piece doesn't already demonstrate.

Workflow (new piece)

Step 1: RESEARCH

  • Search the web for the target app's REST API documentation
  • Identify the auth method (API key, OAuth2, Basic Auth, custom)
  • List available endpoints; check if webhooks are supported
  • Note base URL, pagination, and rate limits

Step 2: PLAN

  • Location: packages/pieces/community/ by default; packages/pieces/custom/ only if the user says "custom piece". See Piece Types below.
  • Choose the correct auth type — see Quick Auth Reference below
  • Select the most useful actions (CRUD, search, list) and triggers (webhook if supported, polling otherwise)
  • Ask the user if OAuth2 config is unclear, there are >10 possible actions, or API behavior is ambiguous

Step 3: SCAFFOLD

Create this structure under packages/pieces/community/<name>/:

src/
  index.ts
  lib/
    auth.ts             # Auth always lives here — never inline in index.ts
    actions/            # One file per action
    triggers/           # One file per trigger
    common/             # Shared helpers (optional)
package.json
.eslintrc.json
tsconfig.json
tsconfig.lib.json

package.json

json
{
    "name": "@activepieces/piece-<name>",
    "version": "0.0.1",
    "main": "./dist/src/index.js",
    "types": "./dist/src/index.d.ts",
    "scripts": {
        "build": "tsc -p tsconfig.lib.json && cp package.json dist/",
        "lint": "eslint 'src/**/*.ts'"
    },
    "dependencies": {
        "@activepieces/pieces-common": "workspace:*",
        "@activepieces/pieces-framework": "workspace:*",
        "@activepieces/shared": "workspace:*",
        "tslib": "2.6.2"
    }
}

Add third-party SDKs to dependencies with a pinned version (e.g. "stripe": "18.2.1").

.eslintrc.json

json
{
    "extends": ["../../../../.eslintrc.json"],
    "ignorePatterns": ["!**/*"],
    "overrides": [
        { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {} },
        { "files": ["*.ts", "*.tsx"], "rules": {} },
        { "files": ["*.js", "*.jsx"], "rules": {} }
    ]
}

tsconfig.json

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

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"]
}

Step 4: IMPLEMENT

The condensed rules in this file (Quick Auth Reference, Quick Piece Definition Template, UX Quality, Output Quality) cover the common case. Open a reference file when you need a concrete copy-ready example for the specific pattern you're building.

When you need a pattern, read the relevant reference file — do not grep other pieces in the codebase. The reference files contain copy-ready examples for every common case. Searching packages/pieces/community/ surfaces inconsistent older code and wastes context.

When you reach for itOpen this file
Wiring auth beyond the Quick Auth Reference tableauth-patterns.md
Your first action in this piece (full file shape)action-patterns.md
A trigger — polling, webhook, handshake, or renewaltrigger-patterns.md
A prop type you haven't used (dropdowns, dynamic, arrays, files)props-patterns.md
Shared API helper, pagination, or createCustomApiCallActioncommon-patterns.md
An advanced UX pattern (source selectors, AWS-style auth)ux-guidelines.md
Flattening a deeply nested API responseoutput-quality.md
Tagging an action/trigger for AI agentsai-metadata.md

Step 5: WIRE & VERIFY

Wiring checklist:

  • Import every action in src/index.ts → add to actions: [...]
  • Import every trigger in src/index.ts → add to triggers: [...]
  • Add createCustomApiCallAction to actions: [...]
  • Register in tsconfig.base.json at repo root (insert alphabetically — build fails without this):
    json
    "@activepieces/piece-<name>": ["packages/pieces/community/<name>/src/index.ts"]
    

Build and lint:

bash
bun install   # new pieces only — creates workspace symlinks
npx turbo run build --filter=@activepieces/piece-<name>
npx turbo run lint --filter=@activepieces/piece-<name>

Both must pass. Lint failures (unused imports, any types, unused vars) block CI even when the build is green.

Common TS errors: missing import in src/index.ts, missing tsconfig.base.json entry, reading context.auth as a plain string for SecretText (use context.auth.secret_text), missing sampleData on a trigger.

Test locally: Add AP_DEV_PIECES=<name> to packages/server/api/.env, start with npm start, open localhost:4200.


Versioning an existing piece

Every change to an existing piece needs a version bump in its package.json. Without it, live flows never pick up your change.

BumpWhen
MAJORRemove an action/trigger/prop; add a required prop to an existing action/trigger; change existing behavior
PATCHAdd a new action or trigger; add an optional prop; add an output attribute; fix a bug

Rule of thumb: any removal is breaking, any new required prop is breaking, everything else is PATCH. When in doubt, prefer MAJOR.


Piece Types

LocationPurpose
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.


Quick Auth Reference

In actions and triggers, context.auth is the resolved connection object — not a flat string:

API Auth MethodActivepieces TypeAccess Pattern
API key / Bearer tokenPieceAuth.SecretText()context.auth.secret_text
OAuth2PieceAuth.OAuth2()context.auth.access_token; extra props via context.auth.props?.['<key>']
Username + passwordPieceAuth.BasicAuth()context.auth.username, context.auth.password
Multiple fieldsPieceAuth.CustomAuth()context.auth.props.<field_name>
No auth neededPieceAuth.None()No context.auth available

Inside the auth's own validate callback the shape is different — it receives the raw entered values (plain string for SecretText, flat object for CustomAuth). The table above applies to action/trigger run() only.

Full code examples: read auth-patterns.md


Quick Piece Definition Template

src/lib/auth.ts

typescript
import { PieceAuth } from '@activepieces/pieces-framework';

export const myAppAuth = PieceAuth.SecretText({
    displayName: 'API Key',
    description: 'Go to Settings > API Keys in your dashboard to generate a key.',
    required: true,
});

src/index.ts

typescript
import { createPiece } from '@activepieces/pieces-framework';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { PieceCategory } from '@activepieces/shared';
import { myAppAuth } from './lib/auth';
import { myAction } from './lib/actions/my-action';
import { myTrigger } from './lib/triggers/my-trigger';

export const myApp = createPiece({
    displayName: 'My App',
    description: 'What the app does in one sentence.',
    minimumSupportedRelease: '0.36.1',
    logoUrl: 'https://cdn.activepieces.com/pieces/my-app.png',
    categories: [PieceCategory.PRODUCTIVITY],
    auth: myAppAuth,
    authors: ['your-github-username'],
    actions: [
        myAction,
        createCustomApiCallAction({
            baseUrl: () => 'https://api.example.com/v1',
            auth: myAppAuth,
            authMapping: async (auth) => ({
                Authorization: `Bearer ${auth.secret_text}`,
            }),
        }),
    ],
    triggers: [myTrigger],
});

UX Quality: Easy for Non-Technical Users

Pieces are used by people who have never seen an API — props, dropdowns, and descriptions must be self-explanatory.

  1. Never ask users to type IDs — Use dynamic dropdowns so they pick items by name ("Jane Doe ([email protected])" not "cus_abc123").
  2. Descriptions must teach — Don't say "Enter the thread timestamp." Say "Click the three dots next to the message, select Copy Link, and paste the number at the end."
  3. Use Markdown instructions for complex setup — Add Property.MarkDown() with numbered steps when a prop requires configuration in the third-party app.
  4. Set sensible defaults — If 90% of users want the same value, make it the default.
  5. Plain language display names"Create Contact" not "POST /contacts". Triggers: "New Order" not "order.created webhook".
  6. Auth descriptions must include step-by-step instructions to get the API key or set up OAuth.
  7. Helpful dropdown placeholders"Please select a project first" not empty.

Full patterns and examples: read ux-guidelines.md


Output Quality: Table-Ready Data

Users pipe piece outputs into Google Sheets and Activepieces Tables constantly — nested or inconsistent output breaks their flows.

  1. Flatten nested objects{ user: { name: "Jo" } }{ user_name: "Jo" }.
  2. Arrays of records must have consistent flat keys — same keys on every object so each maps to a column.
  3. Single-record actions return a flat object. List/search actions return a flat array.
  4. Human-readable key namescompany_name not cName. These become column headers.

Full patterns and examples: read output-quality.md


AI-Ready Metadata (Required on New Actions & Triggers)

  • audience (actions only): 'human' | 'ai' | 'both' — written explicitly on every action ('both' for normal integration actions; 'human' for LLM-wrappers/utilities). Downstream filters only see it when physically present.
  • aiMetadata: { description, idempotent } on every action, { description } on every trigger — agent-facing description (what + when-to-pick + key constraint) and safe-retry hint derived from run().

The catalog is fully curated; a new action or trigger without these is a regression. Writing rules, idempotent derivation, factory gotchas: read ai-metadata.md


Critical Reminders

  1. Register in tsconfig.base.json — alphabetically in compilerOptions.paths. Build fails silently without this.
  2. Action/trigger name fields are permanent — never change them after publishing; flows store them.
  3. Auth lives in src/lib/auth.ts — define there, import in actions/triggers via import { myAppAuth } from '../auth'. Do NOT re-export from index.ts.
  4. Always provide sampleData on triggers — even {}.
  5. Build AND lint must both pass — lint failures (unused imports, any, unused vars) block CI even when build is green.
  6. Bump version on every existing-piece change — see Versioning above. Skipping means flows never get your fix.
  7. AI metadata on every new action & trigger — explicit audience + aiMetadata { description, idempotent } on actions, aiMetadata { description } on triggers. See ai-metadata.md.

When to Ask the User

Pause and ask if:

  • OAuth2 authUrl/tokenUrl/scopes are missing from the API docs
  • Auth method is unclear or undocumented
  • More than 10 possible actions exist — ask which to prioritize
  • API uses webhook signature verification
  • You need test credentials or sandbox access