changed zapier to use oauth

This commit is contained in:
waleed
2025-12-09 14:28:04 -08:00
parent 4d9ae94047
commit 3696658b03
16 changed files with 195 additions and 51 deletions

View File

@@ -262,6 +262,8 @@ const SCOPE_DESCRIPTIONS: Record<string, string> = {
'sharing.write': 'Share files and folders with others',
// WordPress.com scopes
global: 'Full access to manage your WordPress.com sites, posts, pages, media, and settings',
// Zapier AI Actions scopes
'nla:exposed_actions:execute': 'Execute Zapier AI Actions on your behalf',
}
function getScopeDescription(scope: string): string {

View File

@@ -1,13 +1,15 @@
import { ZapierIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode } from '@/blocks/types'
import type { ZapierResponse } from '@/tools/zapier/types'
export const ZapierBlock: BlockConfig<ZapierResponse> = {
type: 'zapier',
name: 'Zapier',
description: 'Execute actions across 7,000+ apps using Zapier AI Actions',
authMode: AuthMode.OAuth,
longDescription:
'Connect to Zapier AI Actions to execute any of 30,000+ actions across 7,000+ apps. Send emails, create documents, update CRMs, post messages, and more - all through natural language instructions. Requires a Zapier AI Actions API key.',
'Connect to Zapier AI Actions to execute any of 30,000+ actions across 7,000+ apps. Send emails, create documents, update CRMs, post messages, and more - all through natural language instructions.',
docsLink: 'https://docs.sim.ai/tools/zapier',
category: 'tools',
bgColor: '#FFFFFF',
@@ -32,11 +34,12 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
value: () => 'execute',
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Zapier AI Actions API key',
password: true,
id: 'credential',
title: 'Zapier Account',
type: 'oauth-input',
serviceId: 'zapier',
requiredScopes: ['openid', 'nla:exposed_actions:execute'],
placeholder: 'Select Zapier account',
required: true,
},
// Execute Action fields
@@ -487,7 +490,7 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
params: (params) => {
const {
operation,
apiKey,
credential,
actionId,
instructions,
params: execParams,
@@ -521,7 +524,7 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
deleteActionId,
} = params
const baseParams: Record<string, any> = { apiKey }
const baseParams: Record<string, any> = { credential }
// Helper to parse JSON params
const parseJsonParams = (jsonParams: any) => {
@@ -622,7 +625,7 @@ export const ZapierBlock: BlockConfig<ZapierResponse> = {
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
apiKey: { type: 'string', description: 'Zapier AI Actions API key' },
credential: { type: 'string', description: 'Zapier OAuth credential' },
// Execute inputs
actionId: { type: 'string', description: 'AI Action ID to execute' },
instructions: { type: 'string', description: 'Plain English instructions for the action' },

View File

@@ -1847,6 +1847,59 @@ export const auth = betterAuth({
}
},
},
// Zapier AI Actions provider
{
providerId: 'zapier',
clientId: env.ZAPIER_CLIENT_ID as string,
clientSecret: env.ZAPIER_CLIENT_SECRET as string,
authorizationUrl: 'https://actions.zapier.com/oauth/authorize/',
tokenUrl: 'https://actions.zapier.com/oauth/token/',
userInfoUrl: 'https://actions.zapier.com/api/v2/check/',
scopes: ['openid', 'nla:exposed_actions:execute'],
responseType: 'code',
pkce: true,
accessType: 'offline',
prompt: 'consent',
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/zapier`,
getUserInfo: async (tokens) => {
try {
logger.info('Fetching Zapier user profile')
// Zapier's check endpoint returns account info when using OAuth
const response = await fetch('https://actions.zapier.com/api/v2/check/', {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
})
if (!response.ok) {
logger.error('Failed to fetch Zapier user info', {
status: response.status,
statusText: response.statusText,
})
throw new Error('Failed to fetch user info')
}
const data = await response.json()
// Zapier check endpoint returns account_id and other info
const userId = data.account_id || data.user_id || `zapier-${Date.now()}`
return {
id: userId.toString(),
name: data.email || 'Zapier User',
email: data.email || `${userId}@zapier.user`,
emailVerified: !!data.email,
createdAt: new Date(),
updatedAt: new Date(),
}
} catch (error) {
logger.error('Error in Zapier getUserInfo:', { error })
return null
}
},
},
],
}),
// Include SSO plugin when enabled

View File

@@ -230,6 +230,8 @@ export const env = createEnv({
ZOOM_CLIENT_SECRET: z.string().optional(), // Zoom OAuth client secret
WORDPRESS_CLIENT_ID: z.string().optional(), // WordPress.com OAuth client ID
WORDPRESS_CLIENT_SECRET: z.string().optional(), // WordPress.com OAuth client secret
ZAPIER_CLIENT_ID: z.string().optional(), // Zapier AI Actions OAuth client ID
ZAPIER_CLIENT_SECRET: z.string().optional(), // Zapier AI Actions OAuth client secret
// E2B Remote Code Execution
E2B_ENABLED: z.string().optional(), // Enable E2B remote code execution

View File

@@ -37,6 +37,7 @@ import {
WebflowIcon,
WordpressIcon,
xIcon,
ZapierIcon,
ZoomIcon,
} from '@/components/icons'
import { env } from '@/lib/core/config/env'
@@ -70,6 +71,7 @@ export type OAuthProvider =
| 'shopify'
| 'zoom'
| 'wordpress'
| 'zapier'
| string
export type OAuthService =
@@ -111,6 +113,7 @@ export type OAuthService =
| 'shopify'
| 'zoom'
| 'wordpress'
| 'zapier'
export interface OAuthProviderConfig {
id: OAuthProvider
name: string
@@ -891,6 +894,23 @@ export const OAUTH_PROVIDERS: Record<string, OAuthProviderConfig> = {
},
defaultService: 'wordpress',
},
zapier: {
id: 'zapier',
name: 'Zapier',
icon: (props) => ZapierIcon(props),
services: {
zapier: {
id: 'zapier',
name: 'Zapier AI Actions',
description: 'Execute actions across 7,000+ apps using Zapier AI Actions.',
providerId: 'zapier',
icon: (props) => ZapierIcon(props),
baseProviderIcon: (props) => ZapierIcon(props),
scopes: ['openid', 'nla:exposed_actions:execute'],
},
},
defaultService: 'zapier',
},
}
/**
@@ -1470,6 +1490,20 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig {
supportsRefreshTokenRotation: false,
}
}
case 'zapier': {
// Zapier AI Actions OAuth - tokens expire after 10 hours
const { clientId, clientSecret } = getCredentials(
env.ZAPIER_CLIENT_ID,
env.ZAPIER_CLIENT_SECRET
)
return {
tokenEndpoint: 'https://actions.zapier.com/oauth/token/',
clientId,
clientSecret,
useBasicAuth: false,
supportsRefreshTokenRotation: true,
}
}
default:
throw new Error(`Unsupported provider: ${provider}`)
}

View File

@@ -11,12 +11,17 @@ export const zapierCreateAiActionTool: ToolConfig<
'Create a new stored AI Action in Zapier. The action can then be executed with zapier_execute_action.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
@@ -79,7 +84,7 @@ export const zapierCreateAiActionTool: ToolConfig<
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
app: params.app,

View File

@@ -10,12 +10,17 @@ export const zapierDeleteAiActionTool: ToolConfig<
description: 'Delete a stored AI Action from Zapier.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
@@ -31,7 +36,7 @@ export const zapierDeleteAiActionTool: ToolConfig<
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
},

View File

@@ -11,12 +11,17 @@ export const zapierExecuteActionTool: ToolConfig<
'Execute a stored AI Action in Zapier. Runs any of the 30,000+ actions across 7,000+ apps that Zapier supports.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
@@ -53,7 +58,7 @@ export const zapierExecuteActionTool: ToolConfig<
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {

View File

@@ -14,12 +14,17 @@ export const zapierGetActionDetailsTool: ToolConfig<
'Get detailed information about a specific action including its required inputs (needs) and outputs (gives).',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
@@ -109,7 +114,7 @@ export const zapierGetActionDetailsTool: ToolConfig<
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
params: params.params || {},

View File

@@ -11,12 +11,17 @@ export const zapierGuessActionsTool: ToolConfig<
'Find relevant Zapier actions using natural language. Searches across 30,000+ actions to find the best matches for your query.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
query: {
type: 'string',
@@ -46,7 +51,7 @@ export const zapierGuessActionsTool: ToolConfig<
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {

View File

@@ -9,12 +9,17 @@ export const zapierListActionsTool: ToolConfig<ZapierListActionsParams, ZapierLi
'List all AI Actions configured in your Zapier account. Returns stored actions that can be executed.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
},
@@ -23,7 +28,7 @@ export const zapierListActionsTool: ToolConfig<ZapierListActionsParams, ZapierLi
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
},

View File

@@ -14,12 +14,17 @@ export const zapierSearchAppActionsTool: ToolConfig<
'Search for available actions within a specific Zapier app. Returns all actions the app supports.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
@@ -59,7 +64,7 @@ export const zapierSearchAppActionsTool: ToolConfig<
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
},

View File

@@ -8,12 +8,17 @@ export const zapierSearchAppsTool: ToolConfig<ZapierSearchAppsParams, ZapierSear
'Search for apps available in Zapier. Returns apps with their available action counts.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
query: {
type: 'string',
@@ -34,7 +39,7 @@ export const zapierSearchAppsTool: ToolConfig<ZapierSearchAppsParams, ZapierSear
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
},

View File

@@ -14,12 +14,17 @@ export const zapierStatelessExecuteTool: ToolConfig<
'Execute any Zapier action directly without creating a stored AI Action first. Provide the app, action, and instructions.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
app: {
type: 'string',
@@ -113,7 +118,7 @@ export const zapierStatelessExecuteTool: ToolConfig<
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => {
const body: Record<string, any> = {

View File

@@ -1,8 +1,8 @@
import type { ToolResponse } from '@/tools/types'
// Base params - all Zapier tools require API key
// Base params - all Zapier tools require OAuth access token
export interface ZapierBaseParams {
apiKey: string
accessToken: string
}
// Parameter constraint for execute action

View File

@@ -10,12 +10,17 @@ export const zapierUpdateAiActionTool: ToolConfig<
description: 'Update an existing stored AI Action configuration in Zapier.',
version: '1.0.0',
oauth: {
required: true,
provider: 'zapier',
},
params: {
apiKey: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Zapier AI Actions API key from actions.zapier.com/credentials',
visibility: 'hidden',
description: 'Access token for Zapier AI Actions API',
},
actionId: {
type: 'string',
@@ -84,7 +89,7 @@ export const zapierUpdateAiActionTool: ToolConfig<
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/json',
'x-api-key': params.apiKey,
Authorization: `Bearer ${params.accessToken}`,
}),
body: (params) => ({
app: params.app,