.agents/skills/piece-builder/auth-patterns.md
Most common. Use for simple APIs that issue a single API key or token.
Inside validate, auth is a plain string. Inside actions/triggers, it's the full connection object — read the secret via context.auth.secret_text.
import { PieceAuth } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
export const myAppAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: 'Get your API key from https://app.example.com/settings/api',
required: true,
validate: async ({ auth }) => {
try {
await httpClient.sendRequest({
method: HttpMethod.GET,
url: 'https://api.example.com/v1/me',
headers: { Authorization: `Bearer ${auth}` },
});
return { valid: true };
} catch (e) {
return { valid: false, error: 'Invalid API Key' };
}
},
});
Access in actions/triggers: context.auth.secret_text (string).
Real example: packages/pieces/community/stripe/src/index.ts
For services like Google, Slack, GitHub that use OAuth2 authorization flows.
import { PieceAuth } from '@activepieces/pieces-framework';
export const myAppAuth = PieceAuth.OAuth2({
required: true,
authUrl: 'https://app.example.com/oauth/authorize',
tokenUrl: 'https://app.example.com/oauth/token',
scope: ['read', 'write'],
// Optional settings:
// pkce: true,
// pkceMethod: 'S256',
// prompt: 'consent',
// grantType: OAuth2GrantType.AUTHORIZATION_CODE,
// authorizationMethod: OAuth2AuthorizationMethod.HEADER,
// extra: { audience: 'https://api.example.com' },
});
Access in actions/triggers:
context.auth.access_token — the OAuth2 access tokencontext.auth.props?.['<key>'] — when the auth defines extra props (e.g. data center, region, subdomain)context.auth.data — the raw token response from the provider (refresh token, scope, etc.)async run(context) {
const token = context.auth.access_token;
const region = context.auth.props?.['region'] as string;
// ...
}
For custom API call actions with OAuth2 — authMapping's auth is already typed from auth: myAppAuth, so read auth.access_token directly, no cast:
createCustomApiCallAction({
baseUrl: () => 'https://api.example.com',
auth: myAppAuth,
authMapping: async (auth) => ({
Authorization: `Bearer ${auth.access_token}`,
}),
})
Real example: packages/pieces/community/github/src/index.ts, packages/pieces/community/zoho-campaigns/ (OAuth2 with extra props)
For APIs using username/password authentication.
import { PieceAuth } from '@activepieces/pieces-framework';
export const myAppAuth = PieceAuth.BasicAuth({
displayName: 'Connection',
required: true,
username: {
displayName: 'Username',
description: 'Your account username',
},
password: {
displayName: 'Password',
description: 'Your account password',
},
validate: async ({ auth }) => {
try {
await httpClient.sendRequest({
method: HttpMethod.GET,
url: 'https://api.example.com/v1/me',
authentication: {
type: AuthenticationType.BASIC,
username: auth.username,
password: auth.password,
},
});
return { valid: true };
} catch (e) {
return { valid: false, error: 'Invalid credentials' };
}
},
});
Access in actions/triggers: context.auth.username, context.auth.password
For APIs needing multiple fields — e.g. instance URL + API key, or region + credentials.
import { PieceAuth, Property } from '@activepieces/pieces-framework';
export const myAppAuth = PieceAuth.CustomAuth({
displayName: 'Connection',
required: true,
props: {
base_url: Property.ShortText({
displayName: 'Instance URL',
description: 'e.g. https://mycompany.example.com',
required: true,
}),
api_key: PieceAuth.SecretText({
displayName: 'API Key',
required: true,
}),
},
validate: async ({ auth }) => {
try {
await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${auth.base_url}/api/v1/me`,
headers: { Authorization: `Bearer ${auth.api_key}` },
});
return { valid: true };
} catch (e) {
return { valid: false, error: 'Invalid connection details' };
}
},
});
Access in actions/triggers: the fields live under props, not on auth directly.
async run(context) {
const baseUrl = context.auth.props.base_url;
const apiKey = context.auth.props.api_key;
// ...
}
Inside validate, the callback receives the flat shape — auth.base_url, auth.api_key. Inside actions/triggers, use context.auth.props.<field>.
Allowed prop types in CustomAuth: ShortText, LongText, SecretText, Number, Checkbox, StaticDropdown, StaticMultiSelectDropdown, MarkDown.
Real example: packages/pieces/community/wordpress/src/index.ts, packages/pieces/community/mattermost/src/index.ts
Use this when the API requires a login call to get a short-lived token (e.g. username/password → JWT). Without caching, every action triggers a login request and can cause 429 rate limit errors.
Add a refresh field to cache the token server-side. Activepieces renews it automatically up to 15 minutes before expiry (clamped to half the token lifetime for short-lived tokens).
export const myAppAuth = PieceAuth.CustomAuth({
displayName: 'Connection',
required: true,
props: {
baseUrl: Property.ShortText({ displayName: 'Instance URL', required: true }),
username: Property.ShortText({ displayName: 'Username', required: true }),
password: PieceAuth.SecretText({ displayName: 'Password', required: true }),
},
validate: async ({ auth }) => {
// validate as usual
},
refresh: {
generate: async ({ auth }) => {
// auth is the flat props shape: auth.baseUrl, auth.username, etc.
const res = await httpClient.sendRequest<{ token: string }>({
method: HttpMethod.POST,
url: `${auth.baseUrl}/api/auth/login`,
body: { username: auth.username, password: auth.password },
});
return {
access_token: res.body.token,
// expires_in: 3600, // optional, seconds — omit if API doesn't return it
};
},
defaultExpiresIn: 3300, // fallback TTL in seconds (default 3300 = 55 min)
},
});
Access in actions/triggers: context.auth.access_token holds the cached token. Still use context.auth.props.<field> for the raw credential fields.
async run(context) {
const token = context.auth.access_token; // server-cached, no login call here
const baseUrl = context.auth.props.baseUrl;
await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${baseUrl}/api/resource`,
headers: { Authorization: `Bearer ${token}` },
});
}
Real example: packages/pieces/community/umami/src/lib/auth.ts
For public APIs or utility pieces that don't need credentials.
// In createPiece():
auth: PieceAuth.None(),
When auth is None:
auth: to createAction() / createTrigger()context.auth in the run functionauth parameterReal example: packages/pieces/core/qrcode/src/index.ts