Skip to main content
Glama

mcp-google-sheets

create-ticket.ts16.6 kB
import { createAction, Property, } from '@activepieces/pieces-framework'; import { AuthenticationType, HttpMethod, httpClient, } from '@activepieces/pieces-common'; import { zendeskAuth } from '../..'; import { organizationIdDropdown, brandIdDropdown, problemTicketIdDropdown, groupIdDropdown } from '../common/props'; type AuthProps = { email: string; token: string; subdomain: string; }; export const createTicketAction = createAction({ auth: zendeskAuth, name: 'create-ticket', displayName: 'Create Ticket', description: 'Create a new ticket in Zendesk.', props: { subject: Property.ShortText({ displayName: 'Subject', description: 'The subject of the ticket (optional - will use first comment text if not provided)', required: false, }), comment_body: Property.LongText({ displayName: 'Comment Body', description: 'The comment body (text). Use this for plain text comments.', required: false, }), comment_html_body: Property.LongText({ displayName: 'Comment HTML Body', description: 'The comment body (HTML). Use this for HTML formatted comments. If provided, this takes precedence over Comment Body.', required: false, }), requester_email: Property.ShortText({ displayName: 'Requester Email', description: 'Email address of the ticket requester. If not provided, the authenticated user will be the requester.', required: false, }), requester_name: Property.ShortText({ displayName: 'Requester Name', description: 'Name of the ticket requester (used when creating a new user).', required: false, }), assignee_email: Property.ShortText({ displayName: 'Assignee Email', description: 'Email address of the agent to assign the ticket to.', required: false, }), priority: Property.StaticDropdown({ displayName: 'Priority', description: 'The priority of the ticket.', required: false, options: { disabled: false, placeholder: 'Select priority (optional)', options: [ { label: 'Low', value: 'low' }, { label: 'Normal', value: 'normal' }, { label: 'High', value: 'high' }, { label: 'Urgent', value: 'urgent' }, ], }, }), type: Property.StaticDropdown({ displayName: 'Type', description: 'The type of ticket.', required: false, options: { disabled: false, placeholder: 'Select type (optional)', options: [ { label: 'Problem', value: 'problem' }, { label: 'Incident', value: 'incident' }, { label: 'Question', value: 'question' }, { label: 'Task', value: 'task' }, ], }, }), status: Property.StaticDropdown({ displayName: 'Status', description: 'The status of the ticket.', required: false, options: { disabled: false, placeholder: 'Select status (optional)', options: [ { label: 'New', value: 'new' }, { label: 'Open', value: 'open' }, { label: 'Pending', value: 'pending' }, { label: 'Hold', value: 'hold' }, { label: 'Solved', value: 'solved' }, { label: 'Closed', value: 'closed' }, ], }, }), tags: Property.Array({ displayName: 'Tags', description: 'Array of tags to apply to the ticket.', required: false, }), organization_id: organizationIdDropdown, group_id: groupIdDropdown, external_id: Property.ShortText({ displayName: 'External ID', description: 'An external ID for the ticket (useful for integrations).', required: false, }), collaborator_emails: Property.Array({ displayName: 'Collaborator Emails', description: 'Array of email addresses to add as collaborators.', required: false, }), follower_emails: Property.Array({ displayName: 'Follower Emails', description: 'Array of email addresses to add as followers.', required: false, }), due_at: Property.DateTime({ displayName: 'Due Date', description: 'The date and time when the ticket is due.', required: false, }), custom_fields: Property.DynamicProperties({ displayName: 'Custom Fields', description: 'Custom ticket field values', required: false, refreshers: ['auth'], props: async ({ auth }) => { if (!auth) { return {}; } try { const authentication = auth as AuthProps; const response = await httpClient.sendRequest({ url: `https://${authentication.subdomain}.zendesk.com/api/v2/ticket_fields.json`, method: HttpMethod.GET, authentication: { type: AuthenticationType.BASIC, username: authentication.email + '/token', password: authentication.token, }, }); const fields = (response.body as { ticket_fields: Array<{ id: number; key: string; title: string; description?: string; type: string; active: boolean; removable?: boolean; custom_field_options?: Array<{ name: string; value: string }>; regexp_for_validation?: string; }> }).ticket_fields; const skipSystemTypes = new Set([ 'subject', 'description', 'priority', 'status', 'tickettype', 'group', 'assignee', ]); const dynamicProps: Record<string, any> = {}; for (const field of fields) { if (!field.active) continue; if (skipSystemTypes.has(field.type)) continue; const fieldKey = `field_${field.key ?? `custom_field_${field.id}`}`; const displayName = field.title; const description = field.description || `Custom ${field.type} field`; switch (field.type) { case 'tagger': if (field.custom_field_options && field.custom_field_options.length > 0) { dynamicProps[fieldKey] = Property.StaticDropdown({ displayName, description, required: false, options: { disabled: false, placeholder: `Select ${displayName}`, options: field.custom_field_options.map(option => ({ label: option.name, value: option.value, })), }, }); } break; case 'multiselect': if (field.custom_field_options && field.custom_field_options.length > 0) { dynamicProps[fieldKey] = Property.StaticMultiSelectDropdown({ displayName, description, required: false, options: { options: field.custom_field_options.map(option => ({ label: option.name, value: option.value, })), }, }); } break; case 'text': dynamicProps[fieldKey] = Property.ShortText({ displayName, description, required: false, }); break; case 'textarea': dynamicProps[fieldKey] = Property.LongText({ displayName, description, required: false, }); break; case 'integer': case 'decimal': dynamicProps[fieldKey] = Property.Number({ displayName, description, required: false, }); break; case 'date': dynamicProps[fieldKey] = Property.DateTime({ displayName, description, required: false, }); break; case 'checkbox': dynamicProps[fieldKey] = Property.Checkbox({ displayName, description, required: false, }); break; case 'regexp': dynamicProps[fieldKey] = Property.ShortText({ displayName, description: `${description}${field.regexp_for_validation ? ` (Pattern: ${field.regexp_for_validation})` : ''}`, required: false, }); break; default: dynamicProps[fieldKey] = Property.ShortText({ displayName, description: `${description} (${field.type})`, required: false, }); } } return dynamicProps; } catch (error) { console.warn('Failed to load ticket fields:', error); return {}; } }, }), comment_public: Property.Checkbox({ displayName: 'Public Comment', description: 'Whether the comment is public (visible to the requester). Defaults to true.', required: false, }), brand_id: brandIdDropdown, forum_topic_id: Property.Number({ displayName: 'Forum Topic ID', description: 'The ID of the forum topic associated with the ticket.', required: false, }), problem_id: problemTicketIdDropdown, }, async run({ propsValue, auth }) { const authentication = auth as AuthProps; const { subject, comment_body, comment_html_body, requester_email, requester_name, assignee_email, priority, type, status, tags, organization_id, group_id, external_id, collaborator_emails, follower_emails, due_at, custom_fields, comment_public, brand_id, forum_topic_id, problem_id, } = propsValue; if (!comment_body && !comment_html_body) { throw new Error('Either Comment Body or Comment HTML Body is required'); } const comment: Record<string, unknown> = {}; if (comment_html_body) { comment.html_body = comment_html_body; } else if (comment_body) { comment.body = comment_body; } if (comment_public !== undefined) { comment.public = comment_public; } const ticket: Record<string, unknown> = { comment, }; if (subject) { ticket.subject = subject; } const resolveUserByEmail = async (email: string) => { try { const response = await httpClient.sendRequest({ url: `https://${authentication.subdomain}.zendesk.com/api/v2/users/search.json?query=email:${encodeURIComponent(email)}`, method: HttpMethod.GET, authentication: { type: AuthenticationType.BASIC, username: authentication.email + '/token', password: authentication.token, }, }); const users = (response.body as { users: Array<{ id: number }> }).users; return users.length > 0 ? users[0].id : null; } catch (error) { console.warn(`Warning: Could not resolve user with email ${email}:`, (error as Error).message); return null; } }; if (requester_email) { const requesterId = await resolveUserByEmail(requester_email); if (requesterId) { ticket.requester_id = requesterId; } else { ticket.requester = { email: requester_email, name: requester_name || requester_email, }; } } if (assignee_email) { const assigneeId = await resolveUserByEmail(assignee_email); if (assigneeId) { ticket.assignee_id = assigneeId; } else { throw new Error(`Could not find agent with email: ${assignee_email}`); } } if (collaborator_emails && Array.isArray(collaborator_emails) && collaborator_emails.length > 0) { const collaboratorIds = []; for (const email of collaborator_emails) { const collaboratorId = await resolveUserByEmail(email as string); if (collaboratorId) { collaboratorIds.push(collaboratorId); } } if (collaboratorIds.length > 0) { ticket.collaborator_ids = collaboratorIds; } } if (follower_emails && Array.isArray(follower_emails) && follower_emails.length > 0) { const followerIds = []; for (const email of follower_emails) { const followerId = await resolveUserByEmail(email as string); if (followerId) { followerIds.push(followerId); } } if (followerIds.length > 0) { ticket.follower_ids = followerIds; } } const optionalParams = { priority, type, status, tags, organization_id, group_id, external_id, due_at, brand_id, forum_topic_id, problem_id, }; for (const [key, value] of Object.entries(optionalParams)) { if (value !== null && value !== undefined && value !== '') { ticket[key] = value; } } if (custom_fields && typeof custom_fields === 'object') { try { const fieldsResponse = await httpClient.sendRequest({ url: `https://${authentication.subdomain}.zendesk.com/api/v2/ticket_fields.json`, method: HttpMethod.GET, authentication: { type: AuthenticationType.BASIC, username: authentication.email + '/token', password: authentication.token, }, }); const fieldDefinitions = (fieldsResponse.body as { ticket_fields: Array<{ id: number; key: string; type: string; }> }).ticket_fields; const customFieldsArray: Array<{ id: number; value: unknown }> = []; for (const [propKey, value] of Object.entries(custom_fields)) { if (value === undefined || value === null || value === '') continue; const fieldKey = propKey.startsWith('field_') ? propKey.substring(6) : propKey; const def = fieldDefinitions.find(f => (f.key ?? `custom_field_${f.id}`) === fieldKey || f.key === fieldKey); if (!def) continue; let formattedValue: unknown = value; if (def.type === 'date' && typeof value === 'string') { formattedValue = new Date(value).toISOString().split('T')[0]; } customFieldsArray.push({ id: def.id, value: formattedValue }); } if (customFieldsArray.length > 0) { ticket.custom_fields = customFieldsArray; } } catch (error) { console.warn('Failed to process custom fields:', error); } } try { const response = await httpClient.sendRequest({ url: `https://${authentication.subdomain}.zendesk.com/api/v2/tickets.json`, method: HttpMethod.POST, headers: { 'Content-Type': 'application/json', }, authentication: { type: AuthenticationType.BASIC, username: authentication.email + '/token', password: authentication.token, }, body: { ticket, }, }); return { success: true, message: 'Ticket created successfully', data: response.body, }; } catch (error) { const errorMessage = (error as Error).message; if (errorMessage.includes('400')) { throw new Error( 'Invalid request parameters. Please check your input values and try again.' ); } if (errorMessage.includes('401') || errorMessage.includes('403')) { throw new Error( 'Authentication failed. Please check your API credentials and permissions.' ); } if (errorMessage.includes('422')) { throw new Error( 'Validation error. Please check that all required fields are provided and valid.' ); } if (errorMessage.includes('429')) { throw new Error( 'Rate limit exceeded. Please wait a moment before trying again.' ); } throw new Error(`Failed to create ticket: ${errorMessage}`); } }, });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server