Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,76 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const asnLookup = createAction({
auth: greipAuth,
name: 'asn_lookup',
displayName: 'ASN Lookup',
description: 'Look up details of an Autonomous System Number (ASN)',
props: {
asn: Property.ShortText({
displayName: 'AS Number',
description: 'The AS Number to lookup (e.g., AS6167 or 6167)',
required: true,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
{ label: 'Newline', value: 'Newline' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { asn, format, mode, callback } = context.propsValue;
const queryParams: Record<string, string> = {
asn: asn,
};
if (format) {
queryParams['format'] = format;
}
if (mode) {
queryParams['mode'] = mode;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/lookup/asn',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,85 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const binLookup = createAction({
auth: greipAuth,
name: 'bin_lookup',
displayName: 'BIN Lookup',
description: 'Look up details of a Bank Identification Number (BIN) or Issuer Identification Number (IIN)',
props: {
bin: Property.ShortText({
displayName: 'BIN/IIN',
description: 'The BIN/IIN of the card (minimum 6 digits). Can be partial like "456789" or full card number',
required: true,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
{ label: 'Newline', value: 'Newline' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
userID: Property.ShortText({
displayName: 'User Identifier',
description: 'Identify requests from specific users for monitoring (e.g., email, phone, user ID)',
required: false,
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { bin, format, mode, userID, callback } = context.propsValue;
const queryParams: Record<string, string> = {
bin: bin,
};
if (format) {
queryParams['format'] = format;
}
if (mode) {
queryParams['mode'] = mode;
}
if (userID) {
queryParams['userID'] = userID;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/lookup/bin',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,85 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const emailValidation = createAction({
auth: greipAuth,
name: 'email_validation',
displayName: 'Email Validation',
description: 'Validate email addresses by checking domain validity, detecting disposable emails, and assessing risk factors',
props: {
email: Property.ShortText({
displayName: 'Email Address',
description: 'The email address to validate',
required: true,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
{ label: 'Newline', value: 'Newline' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
userID: Property.ShortText({
displayName: 'User Identifier',
description: 'Identify requests from specific users for monitoring (e.g., email, phone, user ID)',
required: false,
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { email, format, mode, userID, callback } = context.propsValue;
const queryParams: Record<string, string> = {
email: email,
};
if (format) {
queryParams['format'] = format;
}
if (mode) {
queryParams['mode'] = mode;
}
if (userID) {
queryParams['userID'] = userID;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/scoring/email',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,116 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const ipLookup = createAction({
auth: greipAuth,
name: 'ip_lookup',
displayName: 'IP Lookup',
description: 'Look up comprehensive information about an IP address including location, ISP, security, and risk factors',
props: {
ip: Property.ShortText({
displayName: 'IP Address',
description: 'The IP address to lookup (IPv4 or IPv6)',
required: true,
}),
params: Property.ShortText({
displayName: 'Modules',
description: 'Comma-separated list of modules to include: security, currency, timezone, location (e.g., "security,timezone,currency")',
required: false,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
{ label: 'Newline', value: 'Newline' },
],
},
}),
lang: Property.StaticDropdown({
displayName: 'Response Language',
description: 'Language for the response',
required: false,
defaultValue: 'EN',
options: {
options: [
{ label: 'English', value: 'EN' },
{ label: 'Arabic', value: 'AR' },
{ label: 'German', value: 'DE' },
{ label: 'French', value: 'FR' },
{ label: 'Spanish', value: 'ES' },
{ label: 'Japanese', value: 'JA' },
{ label: 'Chinese', value: 'ZH' },
{ label: 'Russian', value: 'RU' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
userID: Property.ShortText({
displayName: 'User Identifier',
description: 'Identify requests from specific users for monitoring (e.g., email, phone, user ID)',
required: false,
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { ip, params, format, lang, mode, userID, callback } = context.propsValue;
const queryParams: Record<string, string> = {
ip: ip,
};
if (params) {
queryParams['params'] = params;
}
if (format) {
queryParams['format'] = format;
}
if (lang) {
queryParams['lang'] = lang;
}
if (mode) {
queryParams['mode'] = mode;
}
if (userID) {
queryParams['userID'] = userID;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/lookup/ip',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,91 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const phoneValidation = createAction({
auth: greipAuth,
name: 'phone_validation',
displayName: 'Phone Validation',
description: 'Validate phone numbers by checking syntax and assessing validity and operational status',
props: {
phone: Property.ShortText({
displayName: 'Phone Number',
description: 'The phone number to validate (e.g., +12121234567, 0012121234567, 12121234567, or 2121234567)',
required: true,
}),
countryCode: Property.ShortText({
displayName: 'Country Code',
description: 'ISO 3166-1 alpha-2 country code (e.g., US, GB, FR)',
required: true,
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
{ label: 'Newline', value: 'Newline' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
userID: Property.ShortText({
displayName: 'User Identifier',
description: 'Identify requests from specific users for monitoring (e.g., email, phone, user ID)',
required: false,
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { phone, countryCode, format, mode, userID, callback } = context.propsValue;
const queryParams: Record<string, string> = {
phone: phone,
countryCode: countryCode,
};
if (format) {
queryParams['format'] = format;
}
if (mode) {
queryParams['mode'] = mode;
}
if (userID) {
queryParams['userID'] = userID;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/scoring/phone',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,107 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { greipAuth } from '../common/auth';
import { greipApiCall } from '../common/client';
export const profanityDetection = createAction({
auth: greipAuth,
name: 'profanity_detection',
displayName: 'Detect Profanity',
description: 'Detect offensive or inappropriate language in text using machine learning',
props: {
text: Property.LongText({
displayName: 'Text',
description: 'The text to check for profanity',
required: true,
}),
scoreOnly: Property.StaticDropdown({
displayName: 'Score Only',
description: 'Return only the score and safety status',
required: false,
defaultValue: 'no',
options: {
options: [
{ label: 'No', value: 'no' },
{ label: 'Yes', value: 'yes' },
],
},
}),
listBadWords: Property.StaticDropdown({
displayName: 'List Bad Words',
description: 'Include a list of bad words found in the text',
required: false,
defaultValue: 'no',
options: {
options: [
{ label: 'No', value: 'no' },
{ label: 'Yes', value: 'yes' },
],
},
}),
format: Property.StaticDropdown({
displayName: 'Response Format',
description: 'Format of the response',
required: false,
defaultValue: 'JSON',
options: {
options: [
{ label: 'JSON', value: 'JSON' },
{ label: 'XML', value: 'XML' },
{ label: 'CSV', value: 'CSV' },
],
},
}),
mode: Property.StaticDropdown({
displayName: 'Environment',
description: 'Environment mode for testing or production',
required: false,
defaultValue: 'live',
options: {
options: [
{ label: 'Live', value: 'live' },
{ label: 'Test', value: 'test' },
],
},
}),
callback: Property.ShortText({
displayName: 'JSONP Callback',
description: 'Function name for JSONP response format',
required: false,
}),
},
async run(context) {
const { text, scoreOnly, listBadWords, format, mode, callback } = context.propsValue;
const queryParams: Record<string, string> = {
text: text,
};
if (scoreOnly) {
queryParams['scoreOnly'] = scoreOnly;
}
if (listBadWords) {
queryParams['listBadWords'] = listBadWords;
}
if (format) {
queryParams['format'] = format;
}
if (mode) {
queryParams['mode'] = mode;
}
if (callback) {
queryParams['callback'] = callback;
}
return await greipApiCall({
method: HttpMethod.GET,
path: '/scoring/profanity',
queryParams,
auth: context.auth,
});
},
});

View File

@@ -0,0 +1,53 @@
import { HttpMethod, httpClient } from '@activepieces/pieces-common';
import { PieceAuth } from '@activepieces/pieces-framework';
const BASE_URL = 'https://greipapi.com';
export const greipAuth = PieceAuth.SecretText({
displayName: 'API Key',
description: `
To get your API Key:
1. Go to your Greip account dashboard
2. Navigate to API settings
3. Copy your API key
4. Paste it here
`,
required: true,
validate: async ({ auth }) => {
if (!auth) {
return {
valid: false,
error: 'API key is required',
};
}
try {
const response = await httpClient.sendRequest({
method: HttpMethod.GET,
url: `${BASE_URL}/geoip`,
headers: {
Authorization: `Bearer ${auth}`,
'Content-Type': 'application/json',
},
});
if (response.status === 200) {
return {
valid: true,
};
}
return {
valid: false,
error: 'Invalid API key',
};
} catch (error: any) {
return {
valid: false,
error: 'Invalid API key. Please check your API key and try again.',
};
}
},
});

View File

@@ -0,0 +1,143 @@
import { HttpMethod, httpClient, HttpMessageBody, QueryParams } from '@activepieces/pieces-common';
import { AppConnectionValueForAuthProperty, PiecePropValueSchema } from '@activepieces/pieces-framework';
import { greipAuth } from './auth';
const BASE_URL = 'https://greipapi.com';
export type GreipApiCallParams = {
method: HttpMethod;
path: string;
queryParams?: Record<string, string | number | string[] | undefined>;
body?: any;
auth: AppConnectionValueForAuthProperty<typeof greipAuth>;
};
export async function greipApiCall<T extends HttpMessageBody>({
method,
path,
queryParams,
body,
auth,
}: GreipApiCallParams): Promise<T> {
const url = `${BASE_URL}${path}`;
const headers: Record<string, string> = {
Authorization: `Bearer ${auth.secret_text}`,
'Content-Type': 'application/json',
};
const qs: QueryParams = {};
if (queryParams) {
for (const [key, value] of Object.entries(queryParams)) {
if (value !== null && value !== undefined) {
qs[key] = String(value);
}
}
}
try {
const response = await httpClient.sendRequest<T>({
method,
url,
headers,
queryParams: qs,
body,
});
// Check if response body indicates an error
if (response.body && typeof response.body === 'object' && 'status' in response.body && response.body.status === 'error') {
const errorBody = response.body as { code?: number; type?: string; description?: string };
throw new Error(errorBody.description || `Greip API error (code: ${errorBody.code || 'unknown'})`);
}
return response.body;
} catch (error: any) {
// Handle Greip API error response structure
let errorBody = error.response?.body || error.body;
// Handle stringified JSON error bodies
if (typeof errorBody === 'string') {
try {
errorBody = JSON.parse(errorBody);
} catch {
// If parsing fails, use the string as-is
}
}
// Check if it's a Greip error response
if (errorBody && typeof errorBody === 'object') {
// Check for nested error structure (error.response.body)
if (errorBody.response?.body) {
errorBody = errorBody.response.body;
}
if ('status' in errorBody && errorBody.status === 'error') {
const greipError = errorBody as { code?: number; type?: string; description?: string };
const description = greipError.description || getErrorMessageByCode(greipError.code);
throw new Error(description || `Greip API error (code: ${greipError.code || 'unknown'})`);
}
// Also check for description directly in errorBody
if ('description' in errorBody && errorBody.description) {
throw new Error(String(errorBody.description));
}
}
const statusCode = error.response?.status || error.status;
if (statusCode === 401 || statusCode === 403) {
throw new Error('Authentication failed. Please check your API key.');
}
if (statusCode === 429) {
throw new Error('Rate limit exceeded. Please wait a moment and try again.');
}
if (statusCode === 404) {
throw new Error('Endpoint not found. Please check the API endpoint path.');
}
if (statusCode >= 400 && statusCode < 500) {
const errorMessage = errorBody?.description || errorBody?.message || errorBody?.error || error.message || 'Request failed';
throw new Error(`Request failed: ${errorMessage}`);
}
// Fallback: include original error message if available
const originalMessage = error.message || String(error);
throw new Error(`Greip API error: ${originalMessage}`);
}
}
function getErrorMessageByCode(code?: number): string {
const errorMessages: Record<number, string> = {
101: 'The API Key is missing or invalid.',
102: 'The API Key owner account is inactive. Please contact support.',
103: 'You reached the usage limit of your account. Please upgrade your subscription.',
104: 'Invalid parameters. Please check the parameter values.',
105: 'Your plan has expired. Renew the subscription to enable using the API.',
106: 'Too many requests detected. Please slow down your request rate.',
107: 'The callback parameter value cannot be a function name.',
108: 'Invalid format. Use JSON, XML, CSV, or Newline.',
109: 'Callback feature can only be used with JSON format.',
110: 'Invalid language. Use EN, AR, FR, DE, ES, JA, ZH, or RU.',
111: 'Invalid mode. Use test or live.',
112: 'The IP Address is not valid or empty.',
113: 'Request sent from a domain that is not whitelisted in your API settings.',
114: 'Security module is not available in the free plan. Please upgrade.',
115: 'An error occurred while processing your request. Please try again later.',
116: 'The Country Code is invalid or not found.',
117: 'This feature is not available for your plan. Please upgrade.',
118: 'The Phone Number is invalid or missing.',
119: 'The Email Address is invalid or missing.',
120: 'The BIN number is invalid or missing.',
121: 'The AS Number is empty or invalid.',
122: 'The IBAN is invalid or missing.',
123: 'The user identifier is invalid or too long.',
124: 'The user type is invalid or missing. Use email, phone, or user_id.',
125: 'The user value is invalid or missing.',
126: 'You have reached the limit of deletions for this day. Please wait until the next day.',
};
return errorMessages[code || 0] || 'An unknown error occurred.';
}

View File

@@ -0,0 +1,62 @@
import {
createTrigger,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { greipAuth } from '../common/auth';
export const fraudulentPaymentDetectedTrigger = createTrigger({
name: 'fraudulent_payment_detected',
displayName: 'Fraudulent Payment Detected',
description: 'Triggers when a new fraudulent payment is detected by Greip',
auth: greipAuth,
props: {
webhookInstructions: Property.MarkDown({
value: `
To use this trigger, you need to manually set up a webhook in your Greip account:
1. Login to your Greip dashboard.
2. Go to Settings > Integrations > Webhooks.
3. Click on "Add Webhook" or "Create New Webhook".
4. Add the following URL in the **Webhook URL** field:
\`\`\`text
{{webhookUrl}}
\`\`\`
5. Select **fraud_payment** from the event types.
6. Click Save to create the webhook.
**Note:** Webhooks are only available for paid subscriptions.
`,
}),
},
type: TriggerStrategy.WEBHOOK,
sampleData: {
event: 'fraud_payment',
customer_id: 'UID123',
customer_email: 'name@domain.com',
customer_phone: '0555123456',
score: 31.666666666666664,
rules: [
{
id: 'PF10001',
description: 'High purchase rate, according to `customer_ip`.',
},
{
id: 'PF10002',
description: 'High purchase rate, according to `customer_id`.',
},
],
rulesChecked: 6,
rulesDetected: 2,
},
async onEnable(context) {
// Webhooks are set up manually in Greip dashboard
},
async onDisable(context) {
// Webhooks are removed manually in Greip dashboard
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,50 @@
import {
createTrigger,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { greipAuth } from '../common/auth';
export const profanityTextDetectedTrigger = createTrigger({
name: 'profanity_text_detected',
displayName: 'Profanity Text Detected',
description: 'Triggers when Greip detects profanity in a specific text',
auth: greipAuth,
props: {
webhookInstructions: Property.MarkDown({
value: `
To use this trigger, you need to manually set up a webhook in your Greip account:
1. Login to your Greip dashboard.
2. Go to Settings > Integrations > Webhooks.
3. Click on "Add Webhook" or "Create New Webhook".
4. Add the following URL in the **Webhook URL** field:
\`\`\`text
{{webhookUrl}}
\`\`\`
5. Select **profanity** from the event types.
6. Click Save to create the webhook.
**Note:** Webhooks are only available for paid subscriptions.
`,
}),
},
type: TriggerStrategy.WEBHOOK,
sampleData: {
event: 'profanity',
text: '**** *****',
totalBadWords: 2,
riskScore: 1,
isSafe: false,
},
async onEnable(context) {
// Webhooks are set up manually in Greip dashboard
},
async onDisable(context) {
// Webhooks are removed manually in Greip dashboard
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,60 @@
import {
createTrigger,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { greipAuth } from '../common/auth';
export const proxyConnectionDetectedTrigger = createTrigger({
name: 'proxy_connection_detected',
displayName: 'Proxy Connection Detected',
description: 'Triggers when a new proxy connection is detected',
auth: greipAuth,
props: {
webhookInstructions: Property.MarkDown({
value: `
To use this trigger, you need to manually set up a webhook in your Greip account:
1. Login to your Greip dashboard.
2. Go to Settings > Integrations > Webhooks.
3. Click on "Add Webhook" or "Create New Webhook".
4. Add the following URL in the **Webhook URL** field:
\`\`\`text
{{webhookUrl}}
\`\`\`
5. Select **proxy_detected** from the event types.
6. Click Save to create the webhook.
**Note:** Webhooks are only available for paid subscriptions.
`,
}),
},
type: TriggerStrategy.WEBHOOK,
sampleData: {
event: 'proxy_detected',
ip: '1.1.1.1',
ipType: 'IPv4',
IPNumber: 16843009,
countryCode: 'US',
countryGeoNameID: 6252001,
countryName: 'United States',
security: {
isProxy: false,
proxyType: null,
isTor: false,
isBot: false,
isRelay: false,
isHosting: false,
},
},
async onEnable(context) {
// Webhooks are set up manually in Greip dashboard
},
async onDisable(context) {
// Webhooks are removed manually in Greip dashboard
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,50 @@
import {
createTrigger,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { greipAuth } from '../common/auth';
export const spamEmailDetectedTrigger = createTrigger({
name: 'spam_email_detected',
displayName: 'Spam Email Detected',
description: 'Triggers when a new email is marked as SPAM by Greip',
auth: greipAuth,
props: {
webhookInstructions: Property.MarkDown({
value: `
To use this trigger, you need to manually set up a webhook in your Greip account:
1. Login to your Greip dashboard.
2. Go to Settings > Integrations > Webhooks.
3. Click on "Add Webhook" or "Create New Webhook".
4. Add the following URL in the **Webhook URL** field:
\`\`\`text
{{webhookUrl}}
\`\`\`
5. Select **spam_email** from the event types.
6. Click Save to create the webhook.
**Note:** Webhooks are only available for paid subscriptions.
`,
}),
},
type: TriggerStrategy.WEBHOOK,
sampleData: {
event: 'spam_email',
email: 'name@domain.com',
score: 3,
reason: 'Email domain\'s SPF record is not set properly.',
isValid: false,
},
async onEnable(context) {
// Webhooks are set up manually in Greip dashboard
},
async onDisable(context) {
// Webhooks are removed manually in Greip dashboard
},
async run(context) {
return [context.payload.body];
},
});

View File

@@ -0,0 +1,51 @@
import {
createTrigger,
Property,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { greipAuth } from '../common/auth';
export const spamPhoneDetectedTrigger = createTrigger({
name: 'spam_phone_detected',
displayName: 'Spam Phone Number Detected',
description: 'Triggers when a new phone number is marked as SPAM by Greip',
auth: greipAuth,
props: {
webhookInstructions: Property.MarkDown({
value: `
To use this trigger, you need to manually set up a webhook in your Greip account:
1. Login to your Greip dashboard.
2. Go to Settings > Integrations > Webhooks.
3. Click on "Add Webhook" or "Create New Webhook".
4. Add the following URL in the **Webhook URL** field:
\`\`\`text
{{webhookUrl}}
\`\`\`
5. Select **spam_phone** from the event types.
6. Click Save to create the webhook.
**Note:** Webhooks are only available for paid subscriptions.
`,
}),
},
type: TriggerStrategy.WEBHOOK,
sampleData: {
event: 'spam_phone',
phone: '0555123456',
countryCode: 'ir',
carrier: '',
reason: 'Invalid phone number structure.',
isValid: false,
},
async onEnable(context) {
// Webhooks are set up manually in Greip dashboard
},
async onDisable(context) {
// Webhooks are removed manually in Greip dashboard
},
async run(context) {
return [context.payload.body];
},
});