n8n-nodes-callov
Version:
n8n node for Callov API - Complete Management System with AI Agent Support
1,173 lines • 64.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Callov = void 0;
class Callov {
constructor() {
this.methods = {
loadOptions: {
async getClients() {
const baseUrl = 'https://zvlknadahupckbpjgvjf.supabase.co/functions/v1';
const returnData = [];
try {
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'callovApi', {
method: 'GET',
url: `${baseUrl}/api-clients?limit=100`,
json: true,
});
const responseData = response;
if (responseData.data && Array.isArray(responseData.data)) {
for (const client of responseData.data) {
const clientData = client;
returnData.push({
name: clientData.full_name,
value: clientData.id,
description: clientData.email || clientData.phone,
});
}
}
}
catch (error) {
// Return empty array if error
}
return returnData;
},
async getTeamMembers() {
const baseUrl = 'https://zvlknadahupckbpjgvjf.supabase.co/functions/v1';
const returnData = [];
try {
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'callovApi', {
method: 'GET',
url: `${baseUrl}/api-team-members?limit=100&is_active=true`,
json: true,
});
const responseData = response;
if (responseData.data && Array.isArray(responseData.data)) {
for (const member of responseData.data) {
const memberData = member;
returnData.push({
name: memberData.full_name,
value: memberData.id,
description: memberData.specialty || memberData.email,
});
}
}
}
catch (error) {
// Return empty array if error
}
return returnData;
},
async getServices() {
const baseUrl = 'https://zvlknadahupckbpjgvjf.supabase.co/functions/v1';
const returnData = [];
try {
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'callovApi', {
method: 'GET',
url: `${baseUrl}/api-services?limit=100`,
json: true,
});
const responseData = response;
if (responseData.data && Array.isArray(responseData.data)) {
for (const service of responseData.data) {
const serviceData = service;
returnData.push({
name: serviceData.name,
value: serviceData.id,
description: `${serviceData.duration_minutes || ''} min - ${serviceData.price || ''}`,
});
}
}
}
catch (error) {
// Return empty array if error
}
return returnData;
},
},
};
this.description = {
displayName: 'Callov',
name: 'callov',
icon: 'file:callov.png',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Interact with Callov API for appointments and clients',
defaults: {
name: 'Callov',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'callovApi',
required: true,
},
],
usableAsTool: true,
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Appointment',
value: 'appointment',
},
{
name: 'Client',
value: 'client',
},
{
name: 'Service',
value: 'service',
},
{
name: 'Team Member',
value: 'teamMember',
},
],
default: 'appointment',
},
// Appointment Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['appointment'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new appointment',
action: 'Create an appointment',
},
{
name: 'Delete',
value: 'delete',
description: 'Cancel an appointment',
action: 'Delete an appointment',
},
{
name: 'Get',
value: 'get',
description: 'Get an appointment by ID',
action: 'Get an appointment',
},
{
name: 'Get Availability',
value: 'getAvailability',
description: 'Get available time slots for a team member',
action: 'Get availability',
},
{
name: 'Get By Client Phone',
value: 'getByClientPhone',
description: 'Get appointments by client phone number',
action: 'Get appointments by client phone',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many appointments',
action: 'Get many appointments',
},
{
name: 'Update',
value: 'update',
description: 'Update an appointment',
action: 'Update an appointment',
},
],
default: 'getAll',
},
// Client Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['client'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new client',
action: 'Create a client',
},
{
name: 'Get By Phone',
value: 'getByPhone',
description: 'Get a client by phone number',
action: 'Get a client by phone',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many clients',
action: 'Get many clients',
},
],
default: 'getAll',
},
// Service Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['service'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new service',
action: 'Create a service',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a service',
action: 'Delete a service',
},
{
name: 'Get',
value: 'get',
description: 'Get a service by ID',
action: 'Get a service',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many services',
action: 'Get many services',
},
{
name: 'Update',
value: 'update',
description: 'Update a service',
action: 'Update a service',
},
],
default: 'getAll',
},
// Team Member Operations
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['teamMember'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get a team member by ID',
action: 'Get a team member',
},
{
name: 'Get Many',
value: 'getAll',
description: 'Get many team members',
action: 'Get many team members',
},
],
default: 'getAll',
},
// Appointment: Get
{
displayName: 'Appointment ID',
name: 'appointmentId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'The ID of the appointment',
},
// Appointment: Get By Client Phone
{
displayName: 'Client Phone',
name: 'clientPhoneNumber',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getByClientPhone'],
},
},
default: '',
placeholder: '(11) 99999-9999 or 11999999999',
description: 'Phone number of the client to search appointments',
},
{
displayName: 'Additional Filters',
name: 'additionalFilters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getByClientPhone'],
},
},
options: [
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Scheduled',
value: 'scheduled',
},
{
name: 'Confirmed',
value: 'confirmed',
},
{
name: 'Completed',
value: 'completed',
},
{
name: 'Canceled',
value: 'canceled',
},
{
name: 'No Show',
value: 'no_show',
},
],
default: '',
description: 'Filter by appointment status',
},
{
displayName: 'Start Date',
name: 'start_date',
type: 'dateTime',
default: '',
description: 'Filter appointments from this date',
},
{
displayName: 'End Date',
name: 'end_date',
type: 'dateTime',
default: '',
description: 'Filter appointments until this date',
},
],
},
// Appointment: Get Availability
{
displayName: 'Team Member ID',
name: 'teamMemberIdAvailability',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getAvailability'],
},
},
default: '',
description: 'UUID of the team member to check availability',
hint: 'Use "Get Many" on Team Members to find the ID',
},
{
displayName: 'Date',
name: 'availabilityDate',
type: 'dateTime',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getAvailability'],
},
},
default: '',
description: 'Date to check availability (YYYY-MM-DD)',
},
{
displayName: 'Duration Minutes',
name: 'durationMinutes',
type: 'number',
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getAvailability'],
},
},
default: 30,
description: 'Duration in minutes (default: 30)',
},
// Appointment: Create
{
displayName: 'Client ID',
name: 'clientId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
default: '',
description: 'UUID of the client. You can also use the "Get Many" operation on Clients to find the ID.',
hint: 'Use dropdown in manual mode or paste UUID when used as AI tool',
},
{
displayName: 'Service ID',
name: 'serviceId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
default: '',
description: 'UUID of the service. You can also use the "Get Many" operation on Services to find the ID.',
hint: 'Use dropdown in manual mode or paste UUID when used as AI tool',
},
{
displayName: 'Team Member ID',
name: 'teamMemberId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
default: '',
description: 'UUID of the team member/professional. You can also use the "Get Many" operation on Team Members to find the ID.',
hint: 'Use dropdown in manual mode or paste UUID when used as AI tool',
},
{
displayName: 'Start Time',
name: 'startTime',
type: 'dateTime',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
default: '',
description: 'Start date and time of the appointment',
},
{
displayName: 'End Time',
name: 'endTime',
type: 'dateTime',
required: true,
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
default: '',
description: 'End date and time of the appointment',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['appointment'],
operation: ['create'],
},
},
options: [
{
displayName: 'Notes',
name: 'notes',
type: 'string',
default: '',
description: 'Additional notes for the appointment',
},
],
},
// Appointment: Update
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['appointment'],
operation: ['update'],
},
},
options: [
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Scheduled',
value: 'scheduled',
},
{
name: 'Confirmed',
value: 'confirmed',
},
{
name: 'Completed',
value: 'completed',
},
{
name: 'Canceled',
value: 'canceled',
},
{
name: 'No Show',
value: 'no_show',
},
],
default: 'scheduled',
description: 'Status of the appointment',
},
{
displayName: 'Notes',
name: 'notes',
type: 'string',
default: '',
description: 'Additional notes for the appointment',
},
{
displayName: 'Start Time',
name: 'start_time',
type: 'dateTime',
default: '',
description: 'Start date and time of the appointment',
},
{
displayName: 'End Time',
name: 'end_time',
type: 'dateTime',
default: '',
description: 'End date and time of the appointment',
},
],
},
// Appointment: Get Many
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['appointment', 'client'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['appointment', 'client'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: ['appointment'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'Scheduled',
value: 'scheduled',
},
{
name: 'Confirmed',
value: 'confirmed',
},
{
name: 'Completed',
value: 'completed',
},
{
name: 'Canceled',
value: 'canceled',
},
{
name: 'No Show',
value: 'no_show',
},
],
default: '',
description: 'Filter by appointment status',
},
{
displayName: 'Client Phone',
name: 'client_phone',
type: 'string',
default: '',
placeholder: '(11) 99999-9999 or 11999999999',
description: 'Filter appointments by client phone number (with or without formatting)',
},
{
displayName: 'Team Member ID',
name: 'team_member_id',
type: 'string',
default: '',
description: 'Filter by team member/professional',
},
{
displayName: 'Start Date',
name: 'start_date',
type: 'dateTime',
default: '',
description: 'Filter appointments from this date',
},
{
displayName: 'End Date',
name: 'end_date',
type: 'dateTime',
default: '',
description: 'Filter appointments until this date',
},
],
},
// Client: Get By Phone
{
displayName: 'Phone Number',
name: 'phoneNumber',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['client'],
operation: ['getByPhone'],
},
},
default: '',
placeholder: '(11) 99999-9999 or 11999999999',
description: 'Phone number of the client to search for',
},
// Client: Create
{
displayName: 'Full Name',
name: 'fullName',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['client'],
operation: ['create'],
},
},
default: '',
description: 'Full name of the client',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['client'],
operation: ['create'],
},
},
options: [
{
displayName: 'Email',
name: 'email',
type: 'string',
placeholder: 'name@email.com',
default: '',
description: 'Email address of the client',
},
{
displayName: 'Phone',
name: 'phone',
type: 'string',
default: '',
description: 'Phone number of the client',
},
{
displayName: 'CPF',
name: 'cpf',
type: 'string',
default: '',
description: 'CPF of the client',
},
{
displayName: 'Birth Date',
name: 'birth_date',
type: 'dateTime',
default: '',
description: 'Birth date of the client',
},
{
displayName: 'Address',
name: 'address',
type: 'string',
default: '',
description: 'Address of the client',
},
],
},
// Client: Get Many
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: ['client'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Search',
name: 'search',
type: 'string',
default: '',
description: 'Search by name, email or phone',
},
],
},
// Service: Get/Update/Delete
{
displayName: 'Service ID',
name: 'serviceId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'The ID of the service',
},
// Service: Create
{
displayName: 'Name',
name: 'serviceName',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['create'],
},
},
default: '',
description: 'Name of the service',
},
{
displayName: 'Duration Minutes',
name: 'serviceDuration',
type: 'number',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['create'],
},
},
default: 30,
description: 'Duration of the service in minutes',
},
{
displayName: 'Price',
name: 'servicePrice',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['service'],
operation: ['create'],
},
},
default: '',
placeholder: '50.00',
description: 'Price of the service',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['service'],
operation: ['create'],
},
},
options: [
{
displayName: 'Category',
name: 'category',
type: 'string',
default: '',
description: 'Category of the service (e.g., Hair, Nails)',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Description of the service',
},
{
displayName: 'Team Member IDs',
name: 'team_member_ids',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTeamMembers',
},
default: [],
description: 'Team members who can perform this service',
},
],
},
// Service: Update
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: ['service'],
operation: ['update'],
},
},
options: [
{
displayName: 'Name',
name: 'name',
type: 'string',
default: '',
description: 'Name of the service',
},
{
displayName: 'Duration Minutes',
name: 'duration_minutes',
type: 'number',
default: 30,
description: 'Duration of the service in minutes',
},
{
displayName: 'Price',
name: 'price',
type: 'string',
default: '',
description: 'Price of the service',
},
{
displayName: 'Category',
name: 'category',
type: 'string',
default: '',
description: 'Category of the service',
},
{
displayName: 'Description',
name: 'description',
type: 'string',
default: '',
description: 'Description of the service',
},
{
displayName: 'Is Active',
name: 'is_active',
type: 'boolean',
default: true,
description: 'Whether the service is active',
},
{
displayName: 'Team Member IDs',
name: 'team_member_ids',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getTeamMembers',
},
default: [],
description: 'Team members who can perform this service',
},
],
},
// Service: Get Many - Return All
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['service'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['service'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
// Service: Get Many - Filters
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: ['service'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Category',
name: 'category',
type: 'string',
default: '',
description: 'Filter by category',
},
{
displayName: 'Team Member ID',
name: 'team_member_id',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getTeamMembers',
},
default: '',
description: 'Filter by team member',
},
{
displayName: 'Is Active',
name: 'is_active',
type: 'boolean',
default: true,
description: 'Filter only active services',
},
],
},
// Team Member: Get
{
displayName: 'Team Member ID',
name: 'teamMemberIdGet',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['teamMember'],
operation: ['get'],
},
},
default: '',
description: 'The ID of the team member',
},
// Team Member: Get Many - Return All
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: ['teamMember'],
operation: ['getAll'],
},
},
default: false,
description: 'Whether to return all results or only up to a given limit',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: ['teamMember'],
operation: ['getAll'],
returnAll: [false],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'Max number of results to return',
},
// Team Member: Get Many - Filters
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Filter',
default: {},
displayOptions: {
show: {
resource: ['teamMember'],
operation: ['getAll'],
},
},
options: [
{
displayName: 'Search',
name: 'search',
type: 'string',
default: '',
description: 'Search by name, email or specialty',
},
{
displayName: 'Is Active',
name: 'is_active',
type: 'boolean',
default: true,
description: 'Filter only active team members',
},
],
},
],
};
}
async supplyData(itemIndex) {
const resource = this.getNodeParameter('resource', itemIndex);
const operation = this.getNodeParameter('operation', itemIndex);
const toolDescription = `Callov ${resource} - ${operation}`;
return {
response: {
name: `callov_${resource}_${operation}`,
description: toolDescription,
schema: this.getNodeParameter('$input', itemIndex),
},
};
}
async execute() {
const items = this.getInputData();
const returnData = [];
const resource = this.getNodeParameter('resource', 0);
const operation = this.getNodeParameter('operation', 0);
const baseUrl = 'https://zvlknadahupckbpjgvjf.supabase.co/functions/v1';
for (let i = 0; i < items.length; i++) {
try {
if (resource === 'appointment') {
if (operation === 'create') {
const clientId = this.getNodeParameter('clientId', i);
const serviceId = this.getNodeParameter('serviceId', i);
const teamMemberId = this.getNodeParameter('teamMemberId', i);
const startTime = this.getNodeParameter('startTime', i);
const endTime = this.getNodeParameter('endTime', i);
const additionalFields = this.getNodeParameter('additionalFields', i);
const body = {
client_id: clientId,
service_id: serviceId,
team_member_id: teamMemberId,
start_time: startTime,
end_time: endTime,
...additionalFields,
};
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'callovApi', {
method: 'POST',
url: `${baseUrl}/api-appointments`,
body,
json: true,
});
returnData.push(response);
}
if (operation === 'get') {
const appointmentId = this.getNodeParameter('appointmentId', i);
const response = await this.helpers.httpRequestWithAuthentication.call(this, 'callovApi', {
method: 'GET',
url: `${baseUrl}/api-appointments/${appointmentId}`,
json: true,
});
returnData.push(response);
}
if (operation === 'getByClientPhone') {
const clientPhone = this.getNodeParameter('clientPhoneNumber', i);
const additionalFilters = this.getNodeParameter('a