├── guides ├── concatenate-complete-adresse-in-single-property.md ├── geolocate_by_company_domain.md ├── check_email_and_activity_direction.md ├── sync_contact_to_other_portal.md ├── create_renewal_deal_and_associate_with_company.md ├── hubdb_table_lead_rotation.md ├── translate_contact_property.md └── trigger_firebase_web_push_notification.md ├── samples ├── concatenate-complete-adresse-in-single-property.js ├── archive-contact.js ├── linebreak.js ├── calculate_mql_duration.py ├── remove_new_deal_from_deal_name.js ├── capitalize_name.js ├── complete_deal_associated_tasks.js ├── format_phone.js ├── calculate_contract_end_date.js ├── format_date.js ├── trigger_firebase_web_push_notification.js ├── associate_contact_deals_to_company.js ├── create_deals_based_on_contract_length.js ├── geolocate_by_company_domain.js ├── translate_contact_property.js ├── subscription_deal.js ├── weighted_lead_rotation.js ├── object2db.js ├── dedupe_contact.js ├── hubdb_table_lead_rotation.js ├── sync_contact_to_other_portal.js ├── create_task_with_due_date_relative_to_other_date.js ├── check_email_and_activity_direction.js ├── create_renewal_deal_and_associate_with_company.js ├── japanese_prefecture_cleansing.js └── format_phone_number_to_international.js ├── README.md └── LICENSE /guides/concatenate-complete-adresse-in-single-property.md: -------------------------------------------------------------------------------- 1 | This custom coded action can concatenate all the address properties into a single string and output it as 'completeAddress'. 2 | 3 | It takes the properties 'address', 'city', 'state', 'country' and concatenates them together in a single string and outputs the string as an output named 'completeAddress'. 4 | -------------------------------------------------------------------------------- /guides/geolocate_by_company_domain.md: -------------------------------------------------------------------------------- 1 | This action is based on a company trigger. 2 | - Each time a company is added and the domain name is known. 3 | - We take the domain and converts it to an ip adress with the built in dns Node.js module. 4 | - Then we geolocate the ip address to get a postal address, with the ip-api.com service. 5 | - After that we set those values in the company record : 6 | ```JavaScript 7 | { 8 | "server_location": serverLocation, 9 | "server_provider": serverProvider 10 | } 11 | ``` 12 | -------------------------------------------------------------------------------- /samples/concatenate-complete-adresse-in-single-property.js: -------------------------------------------------------------------------------- 1 | exports.main = (event, callback) => { 2 | const address = event.inputFields['address']; 3 | const city = event.inputFields['city']; 4 | const state = event.inputFields['state']; 5 | const country = event.inputFields['country']; 6 | const zip = event.inputFields['zip']; 7 | 8 | const completeAddress = `${address}, ${city}, ${state}, ${country}, ${zip}`; 9 | callback({ 10 | outputFields: { 11 | completeAddress: completeAddress, 12 | }, 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /guides/check_email_and_activity_direction.md: -------------------------------------------------------------------------------- 1 | Instead of sieving through all the activities on the timeline of a deal record, users can refer to the a custom field that summarizes whether the last email and call activity on the record was outbound or inbound. 2 | 3 | The field can then be used in a report or included in a daily email send. For example, run a scheduled workflow daily that emails sales owners if the deal is not closed. The email will contain a summary of what the last email and call engagement was for that deal. They can use this to decide whether a follow-up is needed. 4 | -------------------------------------------------------------------------------- /samples/archive-contact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For a detailed write up on the use cases for this snippet, see 3 | * https://docs.google.com/document/d/15WA0YBX29ZUE3tdUCu7VnnXaC3A2OaIy4iv7I-snytE/edit?usp=sharing 4 | */ 5 | const hubspot = require('@hubspot/api-client') 6 | 7 | exports.main = (event) => { 8 | const hubspotClient = new hubspot.Client({ 9 | accessToken: process.env.secretName 10 | }); 11 | 12 | // Archive a contact by ID 13 | hubspotClient.crm.contacts.basicApi.archive(event.object.objectId) 14 | .then(resp => { 15 | console.log('Contact archived') 16 | }); 17 | }; 18 | 19 | /** 20 | * Original Author: David Gutla 21 | * Reviewed by: Angus Gibbs 22 | */ 23 | -------------------------------------------------------------------------------- /guides/sync_contact_to_other_portal.md: -------------------------------------------------------------------------------- 1 | This custom coded action can be used to maintain a contact sync between two HubSpot Portals (limited to contact properties). 2 | 3 | For example, If your business is collecting leads on a central portal (Portal A for this example) and you have different sales teams working each from their own HubSpot Portal for privacy reasons. 4 | 5 | You can trigger this custom coded action each time a contact is created in the central portal to create a new contact in the target Sales team portal wiht its properties. 6 | 7 | What this code does as well, is that it assigns the correct Owner in the target portal as well. 8 | 9 | This code can serve as inspiration for similar use cases like syncing companies, deals or tickets between multiple hubspot portals. 10 | -------------------------------------------------------------------------------- /samples/linebreak.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client'); 2 | 3 | exports.main = (event, callback) => { 4 | const hubspotClient = new hubspot.Client({ 5 | accessToken: process.env.secretName 6 | }); 7 | 8 | // replace 'multiline' with your multi line text property name 9 | hubspotClient.crm.contacts.basicApi.getById(event.object.objectId, ['multiline']) 10 | .then(results => { 11 | let multiline = results.body.properties.multiline; 12 | console.log(multiline); 13 | multiline = multiline.replace(/(?:\r\n|\r|\n)/g, '
'); 14 | console.log(multiline); 15 | 16 | callback({ 17 | outputFields: { 18 | multiline 19 | } 20 | }); 21 | }) 22 | } 23 | 24 | // Orignal author: Tyler Gunji (https://gist.github.com/dshukertjr) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample use cases for custom code actions in HubSpot workflows 2 | 3 | To highlight the wide range of possible use cases for the custom code actions in HubSpot workflows, we are writing and collecting sample code snippets. If you want to contribute, [please submit your code here](https://form.asana.com/?k=-XZRYnzY1js6ys-_cApQig&d=8587152060687). 4 | 5 | If your use case and code snippets is accepted and reviewed by our team, it will be displayed on our [Programmable Automation Use Case Library](https://www.hubspot.com/programmable-automation-use-cases) 6 | 7 | If you would like more information. Check out our [docs on the custom code action](https://knowledge.hubspot.com/workflows/use-custom-code-actions-in-workflows), as well as the [developer docs](https://developers.hubspot.com/docs/api/workflows/custom-code-actions). 8 | -------------------------------------------------------------------------------- /guides/create_renewal_deal_and_associate_with_company.md: -------------------------------------------------------------------------------- 1 | This custom coded action aims at managing renewals inside of HubSpot. 2 | 3 | Prerequisites: 4 | 2 custom properties specifying the deal applied discount and the deal length in days should be created. 5 | 6 | Trigger: 7 | A new deal gets closed won in a specific pipeline 8 | 9 | Actions: 10 | The Code will follow these steps: 11 | - Takes the original deal amount, deal_length, deal_discount, deal name and close date as input 12 | - Calculates new discount value to be applied to the renewal deal and its new amount 13 | - Calculates new close date of the renewal deal which is the addition of the deal length in days to the original deal close date 14 | - Gets the ID of the primary company associated to the deal 15 | - Creates the renewal deal with the new calculated properties 16 | - Associates the same company associated to the original deal to the renewal deal 17 | -------------------------------------------------------------------------------- /samples/calculate_mql_duration.py: -------------------------------------------------------------------------------- 1 | # Importing date library 2 | 3 | from datetime import datetime 4 | 5 | 6 | def main(event): 7 | # Function to compare both dates 8 | 9 | def days_between(d1, d2): 10 | d1 = datetime.fromtimestamp(d1) 11 | d2 = datetime.fromtimestamp(d2) 12 | return abs((d2 - d1).days) 13 | 14 | # Import and format date fields from HubSpot record 15 | 16 | creationDate = int(event.get('inputFields').get('createdate')) 17 | creationDate_s = creationDate / 1000 18 | mqlDate = int(event.get('inputFields').get('hs_lifecyclestage_marketingqualifiedlead_date')) 19 | mqlDate_s = mqlDate / 1000 20 | leadScoringDuration = days_between(creationDate_s, mqlDate_s) 21 | 22 | # Output calculation of differences in days. 23 | 24 | return { 25 | "outputFields": { 26 | "leadScoringDuration": leadScoringDuration, 27 | } 28 | } 29 | 30 | # Original author: Dom Whittington (https://github.com/DomWhittington) 31 | # Reviewed by: Jan Bogaert 32 | -------------------------------------------------------------------------------- /guides/hubdb_table_lead_rotation.md: -------------------------------------------------------------------------------- 1 | This custom coded action takes a new lead postal code as input. Then provides the ID of the Owner that should be assigned to that lead (an Account Executive for a sales lead for example). 2 | 3 | The Territory management logic will be stored in a HubDB table inside of HubSpot, that for each postal code has a corresponding Email adress of the Owner to assign. 4 | 5 | The Code will follow these steps: 6 | 7 | - Taking Postal code as input 8 | - Query The HubDB correspondance table to retrieve the Email address of the Account Executive responsible for that territory (postal code in here, but the logic can be extended to Postal code ranges) 9 | - Find the HubSpot Owner ID (ID of HS User) corresponding to that Email address 10 | - Store that value in an output field that can be copied to the lead Owner ID field in the next step using "Copy Property Value" action 11 | 12 | As a result, the correct HubSpot User is assigned as owner of that lead. 13 | 14 | This code is a good fit for larger organization having complex territory management logic in place that may result in a very complex and hard to maintain workflow if a workflow is used to manage that. 15 | -------------------------------------------------------------------------------- /guides/translate_contact_property.md: -------------------------------------------------------------------------------- 1 | This custom coded action aims at automatically translating certain contact properties to a certain language. 2 | 3 | It takes the properties to translate as input and gives the translated properties' value as output. 4 | 5 | It uses an API provided from a Tranlation Service called DeepL. Other Translation services APIs can be used. takes a new lead postal code as input. Then provides the ID of the Owner that should be assigned to that lead (an Account Executive for a sales lead for example). 6 | 7 | The Code will follow these steps: 8 | 9 | - Taking the properties to translate as input 10 | - Make an API call to DeepL API to translate the properties while specifying the target language 11 | - Store those values in output fields that can be copied to custom properties in the next step using "Copy Property Value" action 12 | 13 | A example of a use case for this code maybe: 14 | - An organisation is getting leads from multiple sources and wants to make sure all the data in the portal is in a single language 15 | - An organisation using a central hubspot portal to gather data from its differents BUs and wanting to translate all the data to English for example in the central portal 16 | -------------------------------------------------------------------------------- /samples/remove_new_deal_from_deal_name.js: -------------------------------------------------------------------------------- 1 | // Credit: David Gutla https://github.com/gutla 2 | 3 | // This code removes the '- new deal' text added to a deal name after it is created. 4 | const TEXT_TO_REMOVE = ' - New Deal'; 5 | 6 | const hubspot = require('@hubspot/api-client'); 7 | 8 | exports.main = (event, callback) => { 9 | const hubspotClient = new hubspot.Client({ 10 | accessToken: process.env.secretName 11 | }); 12 | 13 | const dealId = event.object.objectId; 14 | 15 | hubspotClient.crm.deals.basicApi.getById(dealId, ['dealname']) 16 | .then(dealResults => { 17 | let dealName = dealResults.body.properties.dealname; 18 | console.log(`Found deal name: ${dealName}`); 19 | 20 | // Remove dealNameText if it is found in the deal title. 21 | if (dealName.includes(TEXT_TO_REMOVE)) { 22 | dealName = dealName.replace(TEXT_TO_REMOVE, '') 23 | console.log(`Removing ${TEXT_TO_REMOVE} from deal name. New value: ${dealName}`); 24 | 25 | // Update deal name with the removed text 26 | hubspotClient.crm.deals.basicApi.update(dealId, { 27 | properties: { 28 | dealname: dealName, 29 | } 30 | }); 31 | } 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /guides/trigger_firebase_web_push_notification.md: -------------------------------------------------------------------------------- 1 | This Custom coded action can be used to trigger a Web Push notification for a specific contact on a web browser. 2 | 3 | This example uses Firebase Cloud Messaging (FCM) which is a service offered by Google to let you relay server messages to registered devices and web apps. The same principle applies for using similar services such as AWS SNS or OneSignal as well as for sending Android/iOS mobile push notifications. 4 | 5 | Prerequisites: 6 | 7 | - A firebase account has been created 8 | - A Firebase web project has been created and web app credentials were generated (for Web push example) 9 | - Firebase was Initialized (on your website) and Service Worker JS was created (essential for triggering notification when website is not loaded) 10 | - The contact (destinator of the push notification) has provided permission to get notifications and as a result, the device token was generated. 11 | 12 | For this example, we assumed the device token was stored on a custom property called "Device Token" on HubSpot contact record. You may consider other alternatives like storing the token on your Website's Database or on HubDB. 13 | 14 | For a video overview of the solution, you can check the following link: https://www.loom.com/share/867b508d45614e7fad22c412a1429d14 15 | -------------------------------------------------------------------------------- /samples/capitalize_name.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Capitalizes the first letter of every part of a contact's first and last name. 3 | * 4 | * Examples: 5 | * - angus -> Angus 6 | * - ANGUS A. -> Angus A. 7 | * - O'connor -> O'Connor 8 | */ 9 | const FIRSTNAME_PROP = "firstname"; 10 | const LASTNAME_PROP = "lastname"; 11 | 12 | const hubspot = require('@hubspot/api-client'); 13 | 14 | exports.main = (event, callback) => { 15 | callback(processEvent(event)); 16 | }; 17 | 18 | function processEvent(event) { 19 | const hubspotClient = new hubspot.Client({ accessToken: process.env.secretName }); 20 | 21 | let contactId = event.object.objectId; 22 | 23 | hubspotClient.crm.contacts.basicApi 24 | .getById(contactId, [FIRSTNAME_PROP, LASTNAME_PROP]) 25 | .then(results => { 26 | let firstname = results.body.properties[FIRSTNAME_PROP]; 27 | let lastname = results.body.properties[LASTNAME_PROP]; 28 | 29 | hubspotClient.crm.contacts.basicApi 30 | .update( 31 | contactId, 32 | { 33 | properties: { 34 | [FIRSTNAME_PROP]: capitalizeName(firstname), 35 | [LASTNAME_PROP]: capitalizeName(lastname) 36 | } 37 | } 38 | ) 39 | .then(updateResults => null); 40 | }); 41 | } 42 | 43 | function capitalizeName(name) { 44 | return name 45 | .toLowerCase() 46 | .trim() 47 | .replace(/\b(\w)/g, s => s.toUpperCase()); 48 | } 49 | -------------------------------------------------------------------------------- /samples/complete_deal_associated_tasks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Originally created by HubSpot community member dirkd in this post: 3 | * https://community.hubspot.com/t5/HubSpot-Ideas/Allow-workflows-to-delete-or-mark-tasks-complete/idc-p/502512#M93728 4 | */ 5 | 6 | const hubspot = require('@hubspot/api-client'); 7 | const request = require("request"); 8 | 9 | exports.main = (event, callback) => { 10 | const hubspotClient = new hubspot.Client({ 11 | accessToken: process.env.secretName 12 | }); 13 | 14 | // Find all tasks associated with deal 15 | hubspotClient.crm.deals.associationsApi.getAll(event.object.objectId, ["task"]) 16 | .then(results => { 17 | // Go through each result returned and set status to COMPLETED 18 | results.body.results.forEach(task => { 19 | var options = { 20 | method: 'PATCH', 21 | url: 'https://api.hubapi.com/engagements/v1/engagements/' + task.id, 22 | qs: { 23 | haccessToken: process.env.secretName 24 | }, 25 | headers: { 26 | 'Content-Type': 'application/json' 27 | }, 28 | body: { 29 | engagement: {}, 30 | metadata: { 31 | status: 'COMPLETED' 32 | } 33 | }, 34 | json: true 35 | }; 36 | 37 | request(options, function (error, response, body) { 38 | if (error) { 39 | throw new Error(error); 40 | } 41 | }); 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /samples/format_phone.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transforms a 10-digit U.S. phone number into a standard format. 3 | */ 4 | const PHONE_NUMBER_PROP = "phone"; 5 | 6 | const hubspot = require('@hubspot/api-client'); 7 | 8 | exports.main = (event, callback) => { 9 | callback(processEvent(event)); 10 | }; 11 | 12 | function processEvent(event) { 13 | const hubspotClient = new hubspot.Client({ accessToken: process.env.secretName }); 14 | 15 | let contactId = event.object.objectId; 16 | 17 | hubspotClient.crm.contacts.basicApi 18 | .getById(contactId, [PHONE_NUMBER_PROP]) 19 | .then(results => { 20 | let phone = results.body.properties[PHONE_NUMBER_PROP]; 21 | 22 | hubspotClient.crm.contacts.basicApi 23 | .update( 24 | contactId, 25 | { 26 | properties: { 27 | [PHONE_NUMBER_PROP]: formatPhoneNumber(phone) 28 | } 29 | } 30 | ) 31 | .then(updateResults => null); 32 | }); 33 | } 34 | 35 | function formatPhoneNumber(phoneNumber) { 36 | let cleaned = phoneNumber.replace(/\D/g, '').trim(); 37 | let match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/); 38 | if (match) { 39 | // Format as (XXX) XXX-XXXX 40 | return '(' + match[1] + ') ' + match[2] + '-' + match[3]; 41 | 42 | // Alternatives (match[1] is the first three digits, 43 | // match[2] is the second three, match[3] is the last four): 44 | // - XXX-XXX-XXXX 45 | // return match[1] + '-' + match[2] + '-' + match[3]; 46 | } 47 | return phoneNumber; 48 | } -------------------------------------------------------------------------------- /samples/calculate_contract_end_date.js: -------------------------------------------------------------------------------- 1 | // The following snippet applies to the Deal object, but can be modified to calculate a property on any Object 2 | // This snippet requires 3 custom properties created in HubSpot: 3 | // "Start Date" (date) / "End Date" (date) / "Duration" (number) 4 | 5 | const hubspot = require('@hubspot/api-client'); 6 | exports.main = (event, callback) => { 7 | 8 | // Set up the HubSpot API client 9 | const hubspotClient = new hubspot.Client({ 10 | accessToken: process.env.secretName 11 | }); 12 | 13 | // Use the client to pull information relating to the currently enrolled deal 14 | hubspotClient.crm.deals.basicApi.getById(event.object.objectId, ["start_date","duration",]) 15 | .then(results => { 16 | 17 | // Store properties in variables 18 | let startDate = results.body.properties.start_date; 19 | let duration = results.body.properties.duration; 20 | 21 | // Calculate the end date from the variables input 22 | let d = new Date(startDate) 23 | let i = parseInt(duration) 24 | let r= new Date(d.setDate(d.getDate()+i)) 25 | r= r.getTime() 26 | 27 | 28 | callback({ 29 | // Store the calculated date in a workflow output - don't forget to copy that value to your "End date" property 30 | outputFields: { 31 | end_date: r 32 | } 33 | }); 34 | }) 35 | .catch(err => { 36 | console.error(err); 37 | }); 38 | } 39 | 40 | // Original author: Theo Burlion (https://gist.github.com/Takazz) 41 | -------------------------------------------------------------------------------- /samples/format_date.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transforms a date string into a standard format. 3 | */ 4 | const DATE_PROP = "createdate"; 5 | 6 | const hubspot = require('@hubspot/api-client'); 7 | 8 | exports.main = (event, callback) => { 9 | callback(processEvent(event)); 10 | }; 11 | 12 | function processEvent(event) { 13 | const hubspotClient = new hubspot.Client({ accessToken: process.env.secretName }); 14 | 15 | let contactId = event.object.objectId; 16 | 17 | hubspotClient.crm.contacts.basicApi 18 | .getById(contactId, ['phone']) 19 | .then(results => { 20 | let dateString = results.body.properties[DATE_PROP]; 21 | 22 | hubspotClient.crm.contacts.basicApi 23 | .update( 24 | contactId, 25 | { 26 | properties: { 27 | [DATE_PROP]: formatDate(dateString) 28 | } 29 | } 30 | ) 31 | .then(updateResults => null); 32 | }); 33 | } 34 | 35 | function formatDate(dateString) { 36 | let date = new Date(dateString); 37 | 38 | let year = '' + date.getFullYear(); 39 | let month = '' + date.getMonth() + 1; 40 | let day = '' + date.getDate(); 41 | 42 | if (month.length < 2) { 43 | month = '0' + month; 44 | } 45 | if (day.length < 2) { 46 | day = '0' + day; 47 | } 48 | 49 | // Format as MM/DD/YYYY 50 | return month + '/' + day + '/' + year; 51 | 52 | // Alternatives: 53 | // - DD/MM/YYYY 54 | // return day + '/' + month + '/' + year; 55 | // - YYYY-MM-DD 56 | // return year + '-' + month + '-' + day; 57 | } -------------------------------------------------------------------------------- /samples/trigger_firebase_web_push_notification.js: -------------------------------------------------------------------------------- 1 | // For instructions on how to use this snippet, see this guide: 2 | // https://github.com/HubSpot/sample-workflow-custom-code/blob/main/guides/trigger_firebase_web_push_notification.md 3 | // Credit: Amine Ben Cheikh Brahim https://gist.github.com/AmineBENCHEIKHBRAHIM 4 | 5 | // Import Axios library for easier HTTP Requests making 6 | const axios = require('axios') 7 | 8 | exports.main = (event, callback) => { 9 | 10 | // Retrieve the Device Token value from the contact enrolled in the current 11 | // workflow. This property should be specified in the "Property to include 12 | // in code" section above the code editor. 13 | const deviceToken = event.inputFields['device_token']; 14 | 15 | // Firebase Server Key should be stored as a Secret to be used in our 16 | // Custom Coded Action. It can be found in the Firebase console. 17 | const headers = { 18 | 'Authorization': 'key=' + process.env.FIREBASE_SERVER_KEY, 19 | 'Content-Type': 'application/json' 20 | }; 21 | 22 | // The request body includes the Device token of the contact and the data that 23 | // will show on the notification itself 24 | const requestBody = { 25 | "to": deviceToken, 26 | "data": { 27 | "body": "Sending Notification Body From Data", 28 | "title": "Notification Title from Data" 29 | } 30 | } 31 | 32 | axios 33 | .post( 34 | 'https://fcm.googleapis.com/fcm/send', 35 | requestBody, 36 | { headers } 37 | ) 38 | .then(response => { 39 | console.log(`Response from Firebase: ${response.body}`); 40 | }); 41 | }; 42 | -------------------------------------------------------------------------------- /samples/associate_contact_deals_to_company.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client'); 2 | 3 | exports.main = (event, callback) => { 4 | const hubspotClient = new hubspot.Client({ 5 | accessToken: process.env.secretName 6 | }); 7 | 8 | hubspotClient.crm.contacts.associationsApi 9 | .getAll(event.object.objectId, 'company') 10 | .then(results => { 11 | if (results.body.results.length == 0) { 12 | console.log("Contact doesn't have any associated companies"); 13 | return; 14 | } 15 | 16 | let companyId = results.body.results[0].id; 17 | console.log(`The contact's associated company ID is ${companyId}`); 18 | 19 | hubspotClient.crm.contacts.associationsApi 20 | .getAll(event.object.objectId, 'deal') 21 | .then(results => { 22 | let associatedDealIds = results.body.results.map(deal => deal.id); 23 | console.log(`The contact's associated deal IDs are ${associatedDealIds.join(', ')}`); 24 | 25 | let batchInput = createBatchBody(companyId, associatedDealIds); 26 | hubspotClient.crm.associations.batchApi 27 | .create('deal', 'company', batchInput) 28 | .then(result => console.log(`Associated ${associatedDealIds.length} deals with the associated company`)); 29 | }); 30 | }); 31 | }; 32 | 33 | function createBatchBody(companyId, dealIds) { 34 | return { 35 | inputs: dealIds.map(dealId => { 36 | return { 37 | from: { 38 | id: dealId 39 | }, 40 | to: { 41 | id: companyId 42 | }, 43 | type: 'deal_to_company' 44 | }; 45 | }) 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /samples/create_deals_based_on_contract_length.js: -------------------------------------------------------------------------------- 1 | // REPLACE WITH YOUR DESIRED DEAL STAGE ID AND PIPELINE ID 2 | const DEAL_STAGE_ID = undefined; 3 | const PIPELINE_ID = undefined; 4 | 5 | const hubspot = require('@hubspot/api-client'); 6 | 7 | exports.main = (event, callback) => { 8 | // Set up the HubSpot API Client 9 | const hubspotClient = new hubspot.Client({ 10 | accessToken: process.env.secretName 11 | }); 12 | 13 | // Use the client to pull information relating to the currently enrolled deal 14 | hubspotClient.crm.deals.basicApi 15 | .getById( 16 | event.object.objectId, 17 | ['contract_length', 'amount', 'closedate', 'dealname'] 18 | ) 19 | .then(results => { 20 | 21 | // Store the appropriate data relating to the currently enrolled deal in variables. 22 | // contract_length is the length of the contract and dictates how many deals we will create. 23 | let contract_length = results.body.properties.contract_length; 24 | let amount = results.body.properties.amount; 25 | let closedate = results.body.properties.closedate; 26 | let dealname = results.body.properties.dealname; 27 | 28 | // Construct our request body 29 | let deals = []; 30 | for (let i = 1; i < contract_length; i++) { 31 | deals.push({ 32 | properties: { 33 | amount, 34 | closedate, 35 | dealname: `MONTH ${i}: ${dealname}`, 36 | dealstage: DEAL_STAGE_ID, 37 | pipeline: PIPELINE_ID 38 | } 39 | }); 40 | } 41 | 42 | // Make a request to batch create X deals 43 | hubspotClient.crm.deals.batchApi.create({ 44 | inputs: deals 45 | }); 46 | }); 47 | } 48 | -------------------------------------------------------------------------------- /samples/geolocate_by_company_domain.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client'); 2 | const axios = require('axios'); 3 | 4 | const dns = require('dns'); 5 | 6 | const lookupPromise = url => 7 | new Promise((resolve, reject) => { 8 | dns.lookup(url, (err, address, family) => { 9 | if (err) reject(err); 10 | resolve(address); 11 | }); 12 | }); 13 | 14 | exports.main = async (event, callback) => { 15 | try { 16 | const companyDomain = event.inputFields.domain; 17 | 18 | if (!companyDomain) 19 | throw new Error(`company domain is undefined we got ${companyDomain}`); 20 | 21 | console.log(`the companyDomain is ${companyDomain}`); 22 | 23 | const ip = await lookupPromise(companyDomain); 24 | 25 | console.log(`ìp for ${companyDomain} is ${ip}`); 26 | 27 | const geoLoc = await axios.get(`http://ip-api.com/json/${ip}`); 28 | 29 | if (!geoLoc.data || geoLoc.data.status !== 'success') 30 | throw new Error(`We failed to geolocate ${ip}... 😬`); 31 | 32 | const serverLocation = `${geoLoc.data.city}, ${geoLoc.data.regionName}, ${geoLoc.data.country}`; 33 | 34 | if (!serverLocation) 35 | throw new Error( 36 | `We can't continue serverLocation is falsy we got ${serverLocation}... 😬` 37 | ); 38 | 39 | const serverProvider = geoLoc.data.isp ? geoLoc.data.isp : ''; 40 | 41 | const properties = { 42 | server_location: serverLocation, 43 | server_provider: serverProvider.toLowerCase(), 44 | }; 45 | 46 | console.log(JSON.stringify(properties)); 47 | 48 | callback({ 49 | outputFields: { 50 | server_location: serverLocation, 51 | server_provider: serverProvider, 52 | }, 53 | }); 54 | } catch (err) { 55 | console.error(err); 56 | // We will automatically retry when the code fails because of a rate limiting error from the HubSpot API. 57 | throw err; 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /samples/translate_contact_property.js: -------------------------------------------------------------------------------- 1 | // For instructions on how to use this snippet, see this guide: 2 | // https://github.com/HubSpot/sample-workflow-custom-code/blob/main/guides/translate_contact_property.md 3 | // Credit: Amine Ben Cheikh Brahim https://gist.github.com/AmineBENCHEIKHBRAHIM 4 | 5 | // Property to translate. For this example, we will translate the "Last NPS Survey Comment" 6 | // property to English. 7 | const PROPERTY_TO_TRANSLATE = 'hs_feedback_last_nps_follow_up'; 8 | 9 | // Import Axios library for easier HTTP request making 10 | const axios = require('axios') 11 | 12 | exports.main = async (event, callback) => { 13 | // Retrieving the property to translate value from the contact enrolled in the current workflow. 14 | // This property should be specified in the "Property to include in code" section above the code editor. 15 | const npsComment = event.inputFields[PROPERTY_TO_TRANSLATE]; 16 | console.log(`Translating text: ${npsComment}`); 17 | 18 | // API Call to DeepL API to translate the property value to British English. 19 | // We didn't specify a source language because the service is capable of detecting the original language automatically. 20 | axios 21 | .post('https://api-free.deepl.com/v2/translate', null, { 22 | params: { 23 | 'auth_key': '' + process.env.DEEPL_KEY, 24 | 'text': '' + npsComment, 25 | 'target_lang': 'EN-GB' 26 | } 27 | }) 28 | .then(response => { 29 | // Retrieve the translated text from the response 30 | let translatedToEnglishText = response.data.translations[0].text; 31 | console.log(translatedToEnglishText); 32 | 33 | // Store the text in an output String called "englishText". It can then be 34 | // copied in a property using "copy property value" workflow action. 35 | callback({ 36 | outputFields: { 37 | englishText: translatedToEnglishText 38 | } 39 | }) 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /samples/subscription_deal.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client') 2 | 3 | /** 4 | * HubSpot will display an error when you paste this code, but it actually 5 | * works if you ignore it and just save it. 6 | * Workflow action to create a new deal on the same close date every month 7 | * The date has to be 1st - 28th. 8 | * If the date is 29th - 31st, the dates will be off at some point. 9 | * 10 | * Also, watch your API limit. This custom action calls the API 3 times everytime it runs. 11 | */ 12 | exports.main = async (event, callback) => { 13 | 14 | const hubspotClient = new hubspot.Client({ 15 | accessToken: process.env.secretName 16 | }); 17 | 18 | const dealId = event.object.objectId 19 | 20 | const results = await hubspotClient.crm.deals.basicApi.getById(dealId, [ 21 | 'closedate', 22 | 'amount', 23 | 'dealname', 24 | 'dealstage', 25 | ]) 26 | 27 | const deal = results.body.properties 28 | 29 | const closeDate = new Date(deal.closedate) 30 | 31 | closeDate.setMonth(closeDate.getMonth() + 1) 32 | closeDate.setMilliseconds(0) 33 | closeDate.setSeconds(0) 34 | closeDate.setMinutes(0) 35 | closeDate.setHours(0) 36 | 37 | const newDealData = { 38 | properties: { 39 | closedate: closeDate.getTime(), 40 | amount: deal.amount, 41 | dealname: deal.dealname, 42 | dealstage: deal.dealstage, 43 | } 44 | } 45 | 46 | const res = await hubspotClient.crm.deals.basicApi.create(newDealData) 47 | 48 | // Associate the new deal with the company that was originally associated with 49 | const newDealId = res.body.id 50 | const associations = await hubspotClient.crm.deals.associationsApi.getAll(dealId, 'companies') 51 | if(associations.body.results.length > 0) { 52 | await hubspotClient.crm.deals.associationsApi.create(newDealId, 'companies', associations.body.results[0].id, 'deal_to_company') 53 | } 54 | 55 | callback({ 56 | outputFields: {} 57 | }) 58 | } 59 | 60 | // Original author: Tyler Gunji (https://gist.github.com/dshukertjr) 61 | -------------------------------------------------------------------------------- /samples/weighted_lead_rotation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Selects a random owner ID with the given relative probability. 3 | * 4 | * To get an owner ID for your HubSpot users, go to Setting -> Properties then 5 | * search for "Contact owner" and click "edit." The "owner ID" is the internal 6 | * value for each user. 7 | * 8 | * To use this value in your workflow, make sure you add an output to your custom 9 | * code action with a data type of "enumeration" and a name of "owner_id" (no quotes). 10 | * Then you can use a "Copy property value" action to update the object owner to 11 | * the user selected by this code. 12 | * 13 | * NOTE: since this is random, it is possible that the actual selections will 14 | * not exactly match the given distribution (in the example, 40%/40%/20%). 15 | * The more times the code is run, the closer it will be to the actual desired 16 | * distribution (this is known as the "Law of Large Numbers"; see 17 | * https://en.wikipedia.org/wiki/Law_of_large_numbers). *This should only be 18 | * used in situations where it is acceptable if the actual selections are not 19 | * exactly the chosen percentages!* 20 | */ 21 | 22 | // Edit percentages and owner IDs here 23 | const owners = [ 24 | { 25 | ownerId: 6090927, 26 | percentage: 40 27 | }, 28 | { 29 | ownerId: 6305635, 30 | percentage: 40 31 | }, 32 | { 33 | ownerId: 6305638, 34 | percentage: 20 35 | } 36 | ]; 37 | 38 | // Start of code 39 | const Promise = require("bluebird"); 40 | const randomNumber = require("random-number-csprng"); 41 | 42 | exports.main = (event, callback) => { 43 | let totalPercentage = 0; 44 | owners.forEach(owner => totalPercentage += owner.percentage); 45 | 46 | Promise 47 | .try(() => randomNumber(1, totalPercentage)) 48 | .then(randomPercentage => { 49 | let currentPercentage = 0; 50 | let selectedOwnerId; 51 | for (let i = 0; i < owners.length; i++) { 52 | currentPercentage += owners[i].percentage; 53 | 54 | if (currentPercentage >= randomPercentage) { 55 | selectedOwnerId = owners[i].ownerId; 56 | break; 57 | } 58 | } 59 | 60 | console.log(`The random value was ${randomPercentage}, which corresponds to owner ID ${selectedOwnerId}`); 61 | 62 | callback({ 63 | outputFields: { 64 | owner_id: selectedOwnerId 65 | } 66 | }); 67 | }); 68 | } -------------------------------------------------------------------------------- /samples/object2db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original author: Cereno Deng 3 | * Source: https://github.com/cerenodeng/object2db 4 | */ 5 | const hubspot = require('@hubspot/api-client'); 6 | 7 | // Must set the yourSecretValue 8 | const hubspotClient = new hubspot.Client({ 9 | accessToken: process.env.yourSecretValue, 10 | }); 11 | 12 | // Must set the table id of HubDB 13 | const dbTableId = 1234567; 14 | 15 | // Return row id if the row with objectId exists or 0 if not exists 16 | const getExistingRowId = async objectId => { 17 | const url = `/cms/v3/hubdb/tables/${dbTableId}/rows?object_id=${objectId}`; 18 | const response = await hubspotClient.apiRequest({ 19 | method: 'get', 20 | path: url, 21 | }); 22 | const json = await response.json(); 23 | return json.total === 0 ? 0 : json.results[0].id; 24 | }; 25 | 26 | const createRow = async values => { 27 | await hubspotClient.cms.hubdb.rowsApi.createTableRow(dbTableId, { values }); 28 | await hubspotClient.cms.hubdb.tablesApi.publishDraftTable(dbTableId); 29 | }; 30 | 31 | const updateRow = async (rowId, values) => { 32 | await hubspotClient.cms.hubdb.rowsApi.updateDraftTableRow(dbTableId, rowId, { 33 | values, 34 | }); 35 | await hubspotClient.cms.hubdb.tablesApi.publishDraftTable(dbTableId); 36 | }; 37 | 38 | exports.main = async event => { 39 | // NOTE: Input field names must match these keys. Ex: hs_object_id 40 | const { 41 | hs_object_id: objectId, 42 | topic, 43 | // set default values for other properties due to only primary display 44 | // property exists after creating a new record 45 | details = '', 46 | date = Date.now(), 47 | speakers = '', 48 | address = '', 49 | } = event.inputFields; 50 | 51 | try { 52 | const rowId = await getExistingRowId(objectId); 53 | // NOTE: HubDB column names must match these keys. Ex: object_id 54 | const values = { 55 | object_id: objectId, 56 | topic, 57 | details, 58 | date: parseInt(date, 10), 59 | speakers, 60 | address, 61 | }; 62 | if (rowId !== 0) { 63 | await updateRow(rowId, values); 64 | } else { 65 | await createRow(values); 66 | } 67 | } catch (err) { 68 | console.error( 69 | err.message === 'HTTP request failed' 70 | ? JSON.stringify(err.response, null, 2) 71 | : err 72 | ); 73 | throw err; 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /samples/dedupe_contact.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Searches for another contact with the same value of DEDUPE_PROPERTY. 3 | * - If no matches are found, nothing happens 4 | * - If one match is found, the enrolled contact is merged into the matching contact 5 | * - If more than one match is found, the action fails 6 | */ 7 | 8 | const DEDUPE_PROPERTY = 'phone'; 9 | 10 | const hubspot = require('@hubspot/api-client'); 11 | 12 | exports.main = (event, callback) => { 13 | // Make sure to add your API key under "Secrets" above. 14 | const hubspotClient = new hubspot.Client({ 15 | accessToken: process.env.secretName 16 | }); 17 | 18 | hubspotClient.crm.contacts.basicApi 19 | .getById(event.object.objectId, [DEDUPE_PROPERTY]) 20 | .then(contactResult => { 21 | let dedupePropValue = contactResult.body.properties[DEDUPE_PROPERTY]; 22 | 23 | console.log(`Looking for duplicates based on ${DEDUPE_PROPERTY} = ${dedupePropValue}`); 24 | hubspotClient.crm.contacts.searchApi 25 | .doSearch({ 26 | filterGroups: [{ 27 | filters: [{ 28 | propertyName: DEDUPE_PROPERTY, 29 | operator: 'EQ', 30 | value: dedupePropValue 31 | }] 32 | }] 33 | }) 34 | .then(searchResults => { 35 | let idsToMerge = searchResults.body.results 36 | .map(object => object.id) 37 | .filter(vid => Number(vid) !== Number(event.object.objectId)); 38 | 39 | if (idsToMerge.length == 0) { 40 | console.log('No matching contact, nothing to merge'); 41 | return; 42 | } else if (idsToMerge.length > 1) { 43 | console.log(`Found multiple potential contact IDs ${idsToMerge.join(', ')} to merge`); 44 | throw new Error("Ambiguous merge; more than one matching contact"); 45 | } 46 | 47 | let idToMerge = idsToMerge[0]; 48 | console.log(`Merging enrolled contact id=${event.object.objectId} into contact id=${idToMerge}`); 49 | hubspotClient 50 | .apiRequest({ 51 | method: 'POST', 52 | path: `/contacts/v1/contact/merge-vids/${idToMerge}`, 53 | body: { 54 | vidToMerge: event.object.objectId 55 | } 56 | }) 57 | .then(mergeResult => { 58 | console.log('Contacts merged!'); 59 | }); 60 | }); 61 | }); 62 | }; 63 | -------------------------------------------------------------------------------- /samples/hubdb_table_lead_rotation.js: -------------------------------------------------------------------------------- 1 | // For instructions on how to use this snippet, see this guide: 2 | // https://github.com/HubSpot/sample-workflow-custom-code/blob/main/guides/hubdb_table_lead_rotation.md 3 | // Credit: Amine Ben Cheikh Brahim https://gist.github.com/AmineBENCHEIKHBRAHIM 4 | 5 | const hubspot = require('@hubspot/api-client'); 6 | 7 | exports.main = (event, callback) => { 8 | // Instantiate HubSpot API Client 9 | const hubspotClient = new hubspot.Client({ 10 | accessToken: process.env.secretName 11 | }); 12 | 13 | // Store the postal code of the current contact enrolld in the workflow 14 | const postalCode = event.inputFields['zip']; 15 | 16 | // If the postal code value is not indicated, throw an error 17 | if (postalCode != null && postalCode != '') { 18 | console.log(`Found a zip code: ${postalCode}. Assignment in progress.`); 19 | } else { 20 | console.log('Contact doesn\'t have a zip code defined, no basis to assign a sales rep'); 21 | throw new Error("Contact doesn't have a zip code"); 22 | } 23 | 24 | // Get the email address of the assigned Account Executive to that postal 25 | // code by querying the HubDB table `postalcodeassociations` 26 | hubspotClient 27 | .apiRequest({ 28 | method: 'GET', 29 | path: `/cms/v3/hubdb/tables/postalcodesassociations/rows?postal_code=` + postalCode, 30 | body: {} 31 | }) 32 | .then(searchResult => { 33 | let salesRepsEmails = searchResult.body.results; 34 | 35 | if (salesRepsEmails.length == 0) { 36 | console.log('No matching sales rep'); 37 | throw new Error('No matching sales rep'); 38 | } 39 | 40 | let salesRepEmail = searchResult.body.results[0].values.sales_rep_email; 41 | console.log(`Sales rep email is ${salesRepEmail}`); 42 | 43 | // Get the ID of the HubSpot user having that email address 44 | hubspotClient.crm.owners.defaultApi 45 | .getPage(salesRepEmail, undefined, 1, false) 46 | .then(owners => { 47 | if (owners.body.results.length == 0) { 48 | console.log('No matching HubSpot user found'); 49 | throw new Error("No matching HubSpot user found"); 50 | } 51 | let ownerId = owners.body.results[0].id; 52 | 53 | console.log(`Sales rep owner ID is ${ownerId}`); 54 | 55 | callback({ 56 | outputFields: { 57 | owner_id: ownerId 58 | } 59 | }) 60 | }); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /samples/sync_contact_to_other_portal.js: -------------------------------------------------------------------------------- 1 | // For instructions on how to use this snippet, see this guide: 2 | // https://github.com/HubSpot/sample-workflow-custom-code/blob/main/guides/sync_contact_to_other_portal.md 3 | // Credit: Amine Ben Cheikh Brahim https://gist.github.com/AmineBENCHEIKHBRAHIM 4 | 5 | const hubspot = require('@hubspot/api-client'); 6 | 7 | exports.main = (event, callback) => { 8 | // Instantiate HubSpot Client that will be used to interface with Source Portal (Portal A) 9 | const hubspotClient = new hubspot.Client({ 10 | accessToken: process.env.secretName 11 | }); 12 | 13 | // Instantiate HubSpot Client that will be used to interface with Destination Portal (Portal B) 14 | const hubspotClient2 = new hubspot.Client({ 15 | apiKey: process.env.TESTPORTALKEY 16 | }); 17 | 18 | // Store the contact properties to sync in variables 19 | hubspotClient.crm.contacts.basicApi 20 | .getById( 21 | event.object.objectId, 22 | ['email', 'firstname', 'lastname', 'phone', 'hubspot_owner_id'] 23 | ) 24 | .then(contact => { 25 | let email = contact.body.properties.email; 26 | let phone = contact.body.properties.phone; 27 | let firstname = contact.body.properties.firstname; 28 | let lastname = contact.body.properties.lastname; 29 | let hubspot_owner_id = contact.body.properties.hubspot_owner_id; 30 | 31 | // Here we assume we would like to assign the same owner in Portal A to the 32 | // copied contact in Portal B and that the owner is a user in both portal A 33 | // and portal B. For example, if john.doe@hubspot.com is a user in Portal A 34 | // and is the owner of the source contact. We would like to assign him as an 35 | // owner of the destination contact in portal B. 36 | 37 | // Get owner email in Portal A 38 | hubspotClient.crm.owners.defaultApi 39 | .getById(hubspot_owner_id, 'id', false) 40 | .then(owners1 => { 41 | let ownerEmail = owners1.body.email; 42 | console.log("original owner ID = " + hubspot_owner_id); 43 | console.log("owner Email = " + ownerEmail); 44 | 45 | // Get the owner ID corresponding to that email in portal B (Because a 46 | // user even though with the same email, will have a different owner ID 47 | // in each portal) 48 | hubspotClient2.crm.owners.defaultApi 49 | .getPage(ownerEmail, undefined, 1, false) 50 | .then(owners2 => { 51 | let ownerId = owners2.body.results[0].id; 52 | console.log("total = " + owners2.body.total); 53 | console.log(JSON.stringify(owners2.body, null, 2)); 54 | console.log("destination owner ID = " + ownerId); 55 | 56 | // Create the contact in portal B and assign the owner to it 57 | hubspotClient2.crm.contacts.basicApi.create({ 58 | "properties": { 59 | "email": email, 60 | "phone": phone, 61 | "firstname": firstname, 62 | "lastname": lastname, 63 | "hubspot_owner_id": ownerId 64 | } 65 | }); 66 | }); 67 | }); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /samples/create_task_with_due_date_relative_to_other_date.js: -------------------------------------------------------------------------------- 1 | // Hubspot Workflow Custom Code for Task Creation with Due Date Calculation 2 | // Copyright (C) 2022, Stefan Woehrer | LEAN-Coders GmbH | https://lean-coders.at 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | 17 | // This code demonstrates how it is possible to create tasks with Custom Code in 18 | // Hubspot Workflows with Due Dates calculated by other properties in hubspot. 19 | // See the following blog article for more information (German): 20 | // https://lean-coders.at/blog/hubspot-workflows-custom-code-zum-erstellen-von-tasks-mit-dynamischem-faelligkeitsdatum 21 | 22 | const hubspot = require('@hubspot/api-client'); 23 | 24 | const createTask = async (hubspotClient, taskBodyObject) => { 25 | const url = `/engagements/v1/engagements`; 26 | hubspotClient.apiRequest({ 27 | path: url, 28 | method: 'POST', 29 | body: taskBodyObject, 30 | json: true 31 | }); 32 | }; 33 | 34 | // Adds "days" days to the timestamp "ts" and returns the new timestamp 35 | const addDaysToTimeStamp = (ts, days) => { 36 | let ret = new Date(ts); 37 | ret.setDate(ret.getDate() + days); 38 | return ret.getTime(); 39 | } 40 | 41 | const buildDealTaskObject = (taskOwner, timestamp, dealId, subject) => { 42 | return { 43 | engagement: { 44 | active: true, 45 | type: 'TASK', 46 | ownerId: taskOwner, 47 | timestamp: timestamp 48 | }, 49 | associations: { 50 | dealIds: [dealId], 51 | }, 52 | metadata: { 53 | body: subject, 54 | subject: subject, 55 | status: "NOT_STARTED", 56 | forObjectType: "DEAL" 57 | } 58 | }; 59 | } 60 | 61 | exports.main = async (event, callback) => { 62 | const hubspotClient = new hubspot.Client({ 63 | accessToken: process.env.secretName 64 | }); 65 | 66 | const taskOwner = parseInt(event.inputFields['hubspot_owner_id']); 67 | const projectStart = parseInt(event.inputFields['start_date']); 68 | const dealId = event.object.objectId; 69 | 70 | const tasksToCreate = [ 71 | buildDealTaskObject(taskOwner, projectStart, dealId, "Task bei Projektstart"), 72 | buildDealTaskObject(taskOwner, addDaysToTimeStamp(projectStart, -5), dealId, "Task 5 Tage vor Projektstart"), 73 | buildDealTaskObject(taskOwner, addDaysToTimeStamp(projectStart, 5), dealId, "Task 5 Tage nach Projektstart"), 74 | ]; 75 | 76 | tasksToCreate.forEach(task => createTask(hubspotClient, task)); 77 | } 78 | -------------------------------------------------------------------------------- /samples/check_email_and_activity_direction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original author: Rebecca Wong 3 | */ 4 | const hubspot = require('@hubspot/api-client'); 5 | 6 | // Format a unix timestamp to the specified language-sensitive representation. 7 | function formatTimestamp(timestamp) { 8 | // Change the locale and options to fit your needs. 9 | const locale = 'en-SG'; 10 | const options = { 11 | timeZone: 'Asia/Singapore', 12 | hour12: false, 13 | }; 14 | return new Date(timestamp).toLocaleDateString(locale, options); 15 | } 16 | 17 | exports.main = async (event, callback) => { 18 | const hubspotClient = new hubspot.Client({ 19 | accessToken: process.env.SECRET_TOKEN, 20 | }); 21 | 22 | // Find the most recent Call associated to the enrolled Deal. 23 | const enrolledDealId = event.object.objectId; 24 | const callsSearchRequest = { 25 | filterGroups: [ 26 | { 27 | filters: [ 28 | { 29 | propertyName: 'associations.deal', 30 | operator: 'EQ', 31 | value: enrolledDealId, 32 | }, 33 | ], 34 | }, 35 | ], 36 | sorts: [ 37 | { 38 | propertyName: 'hs_createdate', 39 | direction: 'DESCENDING', 40 | }, 41 | ], 42 | properties: ['hs_call_direction', 'hs_timestamp'], 43 | limit: 1, // We only need the most recent call. 44 | after: 0, 45 | }; 46 | 47 | const callsResponse = await hubspotClient.apiRequest({ 48 | method: 'POST', 49 | path: '/crm/v3/objects/calls/search', 50 | body: callsSearchRequest, 51 | }); 52 | const callsJson = await callsResponse.json(); 53 | 54 | const [mostRecentCall] = callsJson.results; 55 | let callDirection; 56 | let callTime; 57 | let callMessage; 58 | if (mostRecentCall) { 59 | const { hs_call_direction, hs_timestamp } = mostRecentCall.properties; 60 | callDirection = hs_call_direction; 61 | callTime = formatTimestamp(hs_timestamp); 62 | callMessage = `Last ${callDirection} call at ${callTime}.`; 63 | } else { 64 | callMessage = 'No recent associated calls found.'; 65 | } 66 | console.log(callMessage); 67 | 68 | // Find the most recent, non-forwarded Email associated to the enrolled Deal. 69 | const emailsSearchRequest = { 70 | filterGroups: [ 71 | { 72 | filters: [ 73 | { 74 | propertyName: 'associations.deal', 75 | operator: 'EQ', 76 | value: enrolledDealId, 77 | }, 78 | { 79 | propertyName: 'hs_email_direction', 80 | operator: 'NEQ', 81 | value: 'Forwarded', 82 | }, 83 | ], 84 | }, 85 | ], 86 | sorts: [ 87 | { 88 | propertyName: 'hs_createdate', 89 | direction: 'DESCENDING', 90 | }, 91 | ], 92 | properties: ['hs_email_direction', 'hs_timestamp'], 93 | limit: 1, // We only need the most recent email. 94 | after: 0, 95 | }; 96 | 97 | const emailsResponse = await hubspotClient.apiRequest({ 98 | method: 'POST', 99 | path: '/crm/v3/objects/emails/search', 100 | body: emailsSearchRequest, 101 | }); 102 | const emailsJson = await emailsResponse.json(); 103 | 104 | const [mostRecentEmail] = emailsJson.results; 105 | let emailDirection; 106 | let emailTime; 107 | let emailMessage; 108 | if (mostRecentEmail) { 109 | const { hs_email_direction, hs_timestamp } = mostRecentEmail.properties; 110 | emailDirection = hs_email_direction; 111 | emailTime = formatTimestamp(hs_timestamp); 112 | emailMessage = `Last ${emailDirection} email at ${emailTime}.`; 113 | } else { 114 | emailMessage = 'No recent associated emails found.'; 115 | } 116 | console.log(emailMessage); 117 | 118 | callback({ 119 | outputFields: { 120 | last_call_direction: callDirection, 121 | last_call_time: callTime, 122 | last_email_direction: emailDirection, 123 | last_email_time: emailTime, 124 | message: [callMessage, emailMessage].filter(Boolean).join(''), 125 | }, 126 | }); 127 | }; 128 | -------------------------------------------------------------------------------- /samples/create_renewal_deal_and_associate_with_company.js: -------------------------------------------------------------------------------- 1 | // For instructions on how to use this snippet, see this guide: 2 | // https://github.com/HubSpot/sample-workflow-custom-code/blob/main/guides/create_renewal_deal_and_associate_with_company.md 3 | // Credit: Amine Ben Cheikh Brahim https://gist.github.com/AmineBENCHEIKHBRAHIM 4 | 5 | const hubspot = require('@hubspot/api-client'); 6 | 7 | exports.main = async (event, callback) => { 8 | const hubspotClient = new hubspot.Client({ 9 | accessToken: process.env.secretName 10 | }); 11 | 12 | // Retrieving the properties of the deal to renew 13 | // The properties should be specified in the "Property to include in code" section above the code editor. 14 | // The properties deal_length (in days) and deal_discount (in percentage) should be specified as custom properties on the deal record 15 | const amount = event.inputFields['amount']; 16 | const deal_length = Number(event.inputFields['deal_length']); 17 | const deal_discount = event.inputFields['deal_discount']; 18 | const dealname = event.inputFields['dealname']; 19 | const closedate = event.inputFields['closedate']; 20 | 21 | // If discount and deal length and deal amount values are not indicated, throw an error 22 | if (amount != null && deal_length != null && deal_discount != null) { 23 | console.log( 24 | `discount and deal length and deal amount values are indicated: Renewal deal creation in progress.` 25 | ); 26 | } else { 27 | console.log('discount or deal length or deal amount values are not specified'); 28 | throw new Error("discount or deal length or deal amount values are not specified"); 29 | } 30 | 31 | // Calculate the discount to be applied on the new renewal deal. Here we apply 10% less discount in comparaison to the original deal but feel free to use your own formula. 32 | // If the discount value is negative original discount is less than 10%, the new discount value will be set to 0 33 | var new_deal_discount = deal_discount - 10; 34 | if (new_deal_discount < 0) { 35 | new_deal_discount = 0; 36 | } 37 | 38 | // Calculate the new deal amount after new discount is applied 39 | const new_amount = (((amount * 100) / (100 - deal_discount)) * (100 - new_deal_discount)) / 100; 40 | 41 | // Convert original deal close date unixtimestamp to a number 42 | const unixTimeZero = Number(closedate); 43 | 44 | // Add the deal length in days to the original deal close date to get the renewal deal close date 45 | const newtimestamp = unixTimeZero + deal_length * 1000 * 60 * 60 * 24; 46 | 47 | // Get the ID of the primary company associated to the deal 48 | hubspotClient.crm.deals.associationsApi 49 | .getAll(event.object.objectId, 'company') 50 | .then(results => { 51 | if (results.body.results.length == 0) { 52 | console.log("The Deal doesn't have any associated companies"); 53 | return; 54 | } 55 | 56 | let companyId = results.body.results[0].id; 57 | console.log(`The deals's associated company ID is ${companyId}`); 58 | 59 | // Define the properties of the renewal deal 60 | const properties = { 61 | "amount": new_amount, // Renewal Deal new amount after new discount is applied 62 | "closedate": newtimestamp, // Renewal Deal new close date by adding deal length to the original deal close date 63 | "dealname": dealname + " - Renewal", // Renewal Deal new name by adding - Renewal to the original deal name 64 | "dealstage": 12260673, // Renewal Deal stage 65 | "deal_discount": new_deal_discount, // Renewal deal new discount value 66 | "deal_length": deal_length, // Renewal deal new length. By default, same length as the original deal 67 | "hubspot_owner_id": "49141072", // Renewal deal owner. The value used here is the ID of the CSM responsible for renewal. You'll use your own value generated in your portal 68 | "pipeline": 3630085 // Renewal Deal pipeline. The value used here is the ID of the renewal pipeline. You'll use your own value generated in your portal 69 | }; 70 | const SimplePublicObjectInput = { 71 | properties 72 | }; 73 | 74 | // Create the renewal deal 75 | hubspotClient.crm.deals.basicApi.create(SimplePublicObjectInput).then(DealCreateResponse => { 76 | // Associate the same company associated to the original deal, to the renewal deal 77 | hubspotClient.crm.companies.associationsApi.create(companyId, 'deals', DealCreateResponse 78 | .body.id, 'company_to_deal'); 79 | 80 | }); 81 | }); 82 | } 83 | -------------------------------------------------------------------------------- /samples/japanese_prefecture_cleansing.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client'); 2 | 3 | exports.main = async (event, callback) => { 4 | 5 | const prefectureList = [ 6 | { 7 | id: "01", 8 | name: "北海道", 9 | short: "北海道", 10 | kana: "ホッカイドウ", 11 | en: "hokkaidoken", 12 | enShort: "hokkaido", 13 | }, 14 | { 15 | id: "02", 16 | name: "青森県", 17 | short: "青森", 18 | kana: "アオモリケン", 19 | en: "aomoriken", 20 | enShort: "aomori", 21 | }, 22 | { 23 | id: "03", 24 | name: "岩手県", 25 | short: "岩手", 26 | kana: "イワテケン", 27 | en: "iwateken", 28 | enShort: "iwate", 29 | }, 30 | { 31 | id: "04", 32 | name: "宮城県", 33 | short: "宮城", 34 | kana: "ミヤギケン", 35 | en: "miyagiken", 36 | enShort: "miyagi", 37 | }, 38 | { 39 | id: "05", 40 | name: "秋田県", 41 | short: "秋田", 42 | kana: "アキタケン", 43 | en: "akitaken", 44 | enShort: "akita", 45 | }, 46 | { 47 | id: "06", 48 | name: "山形県", 49 | short: "山形", 50 | kana: "ヤマガタケン", 51 | en: "yamagataken", 52 | enShort: "yamagata", 53 | }, 54 | { 55 | id: "07", 56 | name: "福島県", 57 | short: "福島", 58 | kana: "フクシマケン", 59 | en: "fukushimaken", 60 | enShort: "fukushima", 61 | }, 62 | { 63 | id: "08", 64 | name: "茨城県", 65 | short: "茨城", 66 | kana: "イバラキケン", 67 | en: "ibarakiken", 68 | enShort: "ibaraki", 69 | }, 70 | { 71 | id: "09", 72 | name: "栃木県", 73 | short: "栃木", 74 | kana: "トチギケン", 75 | en: "tochigiken", 76 | enShort: "tochigi", 77 | }, 78 | { 79 | id: "10", 80 | name: "群馬県", 81 | short: "群馬", 82 | kana: "グンマケン", 83 | en: "gunmaken", 84 | enShort: "gunma", 85 | }, 86 | { 87 | id: "11", 88 | name: "埼玉県", 89 | short: "埼玉", 90 | kana: "サイタマケン", 91 | en: "saitamaken", 92 | enShort: "saitama", 93 | }, 94 | { 95 | id: "12", 96 | name: "千葉県", 97 | short: "千葉", 98 | kana: "チバケン", 99 | en: "chibaken", 100 | enShort: "chiba", 101 | }, 102 | { 103 | id: "13", 104 | name: "東京都", 105 | short: "東京", 106 | kana: "トウキョウト", 107 | en: "tokyoken", 108 | enShort: "tokyo", 109 | }, 110 | { 111 | id: "14", 112 | name: "神奈川県", 113 | short: "神奈川", 114 | kana: "カナガワケン", 115 | en: "kanagawaken", 116 | enShort: "kanagawa", 117 | }, 118 | { 119 | id: "15", 120 | name: "新潟県", 121 | short: "新潟", 122 | kana: "ニイガタケン", 123 | en: "niigataken", 124 | enShort: "niigata", 125 | }, 126 | { 127 | id: "16", 128 | name: "富山県", 129 | short: "富山", 130 | kana: "トヤマケン", 131 | en: "toyamaken", 132 | enShort: "toyama", 133 | }, 134 | { 135 | id: "17", 136 | name: "石川県", 137 | short: "石川", 138 | kana: "イシカワケン", 139 | en: "ishikawaken", 140 | enShort: "ishikawa", 141 | }, 142 | { 143 | id: "18", 144 | name: "福井県", 145 | short: "福井", 146 | kana: "フクイケン", 147 | en: "fukuiken", 148 | enShort: "fukui", 149 | }, 150 | { 151 | id: "19", 152 | name: "山梨県", 153 | short: "山梨", 154 | kana: "ヤマナシケン", 155 | en: "yamanashiken", 156 | enShort: "yamanashi", 157 | }, 158 | { 159 | id: "20", 160 | name: "長野県", 161 | short: "長野", 162 | kana: "ナガノケン", 163 | en: "naganoken", 164 | enShort: "nagano", 165 | }, 166 | { 167 | id: "21", 168 | name: "岐阜県", 169 | short: "岐阜", 170 | kana: "ギフケン", 171 | en: "gifuken", 172 | enShort: "gifu", 173 | }, 174 | { 175 | id: "22", 176 | name: "静岡県", 177 | short: "静岡", 178 | kana: "シズオカケン", 179 | en: "shizuokaken", 180 | enShort: "shizuoka", 181 | }, 182 | { 183 | id: "23", 184 | name: "愛知県", 185 | short: "愛知", 186 | kana: "アイチケン", 187 | en: "aichiken", 188 | enShort: "aichi", 189 | }, 190 | { 191 | id: "24", 192 | name: "三重県", 193 | short: "三重", 194 | kana: "ミエケン", 195 | en: "mieken", 196 | enShort: "mie", 197 | }, 198 | { 199 | id: "25", 200 | name: "滋賀県", 201 | short: "滋賀", 202 | kana: "シガケン", 203 | en: "shigaken", 204 | enShort: "shiga", 205 | }, 206 | { 207 | id: "26", 208 | name: "京都府", 209 | short: "京都", 210 | kana: "キョウトフ", 211 | en: "kyotoken", 212 | enShort: "kyoto", 213 | }, 214 | { 215 | id: "27", 216 | name: "大阪府", 217 | short: "大阪", 218 | kana: "オオサカフ", 219 | en: "osakaken", 220 | enShort: "osaka", 221 | }, 222 | { 223 | id: "28", 224 | name: "兵庫県", 225 | short: "兵庫", 226 | kana: "ヒョウゴケン", 227 | en: "hyogoken", 228 | enShort: "hyogo", 229 | }, 230 | { 231 | id: "29", 232 | name: "奈良県", 233 | short: "奈良", 234 | kana: "ナラケン", 235 | en: "naraken", 236 | enShort: "nara", 237 | }, 238 | { 239 | id: "30", 240 | name: "和歌山県", 241 | short: "和歌山", 242 | kana: "ワカヤマケン", 243 | en: "wakayamaken", 244 | enShort: "wakayama", 245 | }, 246 | { 247 | id: "31", 248 | name: "鳥取県", 249 | short: "鳥取", 250 | kana: "トットリケン", 251 | en: "tottoriken", 252 | enShort: "tottori", 253 | }, 254 | { 255 | id: "32", 256 | name: "島根県", 257 | short: "島根", 258 | kana: "シマネケン", 259 | en: "shimaneken", 260 | enShort: "shimane", 261 | }, 262 | { 263 | id: "33", 264 | name: "岡山県", 265 | short: "岡山", 266 | kana: "オカヤマケン", 267 | en: "okayamaken", 268 | enShort: "okayama", 269 | }, 270 | { 271 | id: "34", 272 | name: "広島県", 273 | short: "広島", 274 | kana: "ヒロシマケン", 275 | en: "hiroshimaken", 276 | enShort: "hiroshima", 277 | }, 278 | { 279 | id: "35", 280 | name: "山口県", 281 | short: "山口", 282 | kana: "ヤマグチケン", 283 | en: "yamaguchiken", 284 | enShort: "yamaguchi", 285 | }, 286 | { 287 | id: "36", 288 | name: "徳島県", 289 | short: "徳島", 290 | kana: "トクシマケン", 291 | en: "tokushimaken", 292 | enShort: "tokushima", 293 | }, 294 | { 295 | id: "37", 296 | name: "香川県", 297 | short: "香川", 298 | kana: "カガワケン", 299 | en: "kagawaken", 300 | enShort: "kagawa", 301 | }, 302 | { 303 | id: "38", 304 | name: "愛媛県", 305 | short: "愛媛", 306 | kana: "エヒメケン", 307 | en: "ehimeken", 308 | enShort: "ehime", 309 | }, 310 | { 311 | id: "39", 312 | name: "高知県", 313 | short: "高知", 314 | kana: "コウチケン", 315 | en: "kochiken", 316 | enShort: "kochi", 317 | }, 318 | { 319 | id: "40", 320 | name: "福岡県", 321 | short: "福岡", 322 | kana: "フクオカケン", 323 | en: "fukuokaken", 324 | enShort: "fukuoka", 325 | }, 326 | { 327 | id: "41", 328 | name: "佐賀県", 329 | short: "佐賀", 330 | kana: "サガケン", 331 | en: "sagaken", 332 | enShort: "saga", 333 | }, 334 | { 335 | id: "42", 336 | name: "長崎県", 337 | short: "長崎", 338 | kana: "ナガサキケン", 339 | en: "nagasakiken", 340 | enShort: "nagasaki", 341 | }, 342 | { 343 | id: "43", 344 | name: "熊本県", 345 | short: "熊本", 346 | kana: "クマモトケン", 347 | en: "kumamotoken", 348 | enShort: "kumamoto", 349 | }, 350 | { 351 | id: "44", 352 | name: "大分県", 353 | short: "大分", 354 | kana: "オオイタケン", 355 | en: "oitaken", 356 | enShort: "oita", 357 | }, 358 | { 359 | id: "45", 360 | name: "宮崎県", 361 | short: "宮崎", 362 | kana: "ミヤザキケン", 363 | en: "miyazakiken", 364 | enShort: "miyazaki", 365 | }, 366 | { 367 | id: "46", 368 | name: "鹿児島県", 369 | short: "鹿児島", 370 | kana: "カゴシマケン", 371 | en: "kagoshimaken", 372 | enShort: "kagoshima", 373 | }, 374 | { 375 | id: "47", 376 | name: "沖縄県", 377 | short: "沖縄", 378 | kana: "オキナワケン", 379 | en: "okinawaken", 380 | enShort: "okinawa", 381 | } 382 | ] 383 | 384 | 385 | const hubspotClient = new hubspot.Client({ 386 | accessToken: process.env.secretName 387 | }); 388 | const results = await hubspotClient.crm.contacts.basicApi.getById(event.object.objectId, ['state']) 389 | 390 | /// コンタクトの今の都道府県 391 | const currentState = results.body.properties.state 392 | const lowercaseCurrentState = currentState.toLowerCase().replace(/[ぁ-ん]/g, s => String.fromCharCode(s.charCodeAt(0) + 0x60)); 393 | 394 | /// このアクションのアウトプットが入る変数 395 | let state 396 | 397 | loop1: 398 | for (const prefectureMap of prefectureList) { /// 上の都道府県リスト一個一個をループし、該当する値がないかチェックする 399 | for (const prefectureString of Object.values(prefectureMap)) { 400 | if (prefectureString.includes(lowercaseCurrentState)) { 401 | /** 402 | * 該当する値があった場合はアウトプットする値としてname(日本語表記)をインプットする 403 | * 日本語以外の表記に変換したい場合は'.name'を'.en'や'.enShort'に変えれば英語表記だったりにできる 404 | */ 405 | state = prefectureMap.name 406 | break loop1 407 | } 408 | } 409 | } 410 | 411 | /// もし該当する値がなかった場合は今の都道府県をそのまま返す 412 | if(!state) { 413 | state = currentState 414 | } 415 | 416 | callback({ 417 | outputFields: { 418 | state, 419 | } 420 | }); 421 | } 422 | 423 | // Original author: Tyler Gunji (https://gist.github.com/dshukertjr) 424 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /samples/format_phone_number_to_international.js: -------------------------------------------------------------------------------- 1 | const hubspot = require('@hubspot/api-client'); 2 | 3 | exports.main = (event) => { 4 | const hubspotClient = new hubspot.Client({ 5 | accessToken: process.env.secretName 6 | }); 7 | 8 | // ID of the contact 9 | const id = event.object.objectId 10 | 11 | // GET the Mobile, Phone and Country Property from Contact 12 | hubspotClient.crm.contacts.basicApi.getById(id, ['mobilephone', 'phone', 'country']) 13 | .then(contactPropertiesResults => { 14 | // Set variables and trim any white spaces 15 | const mobile = contactPropertiesResults.body.properties.mobilephone.replace(/\s+/g, ''); 16 | const phone = contactPropertiesResults.body.properties.phone.replace(/\s+/g, ''); 17 | const country = contactPropertiesResults.body.properties.country; 18 | 19 | console.log(`Found mobile=${mobile}, phone=${phone}, country=${country}`); 20 | 21 | // If mobile or phone starts with a 0 and the country value is known, update contact phone numbers to the intl prefix using the formatToIntl method 22 | if (mobile.startsWith('0') && country) { 23 | let intlMobile = formatToIntl(mobile, country); 24 | hubspotClient.crm.contacts.basicApi.update(id, { 25 | properties: { 26 | mobilephone: intlMobile, 27 | } 28 | }); 29 | } 30 | if (phone.startsWith('0') && country) { 31 | let intlPhone = formatToIntl(phone, country); 32 | hubspotClient.crm.contacts.basicApi.update(id, { 33 | properties: { 34 | phone: intlPhone, 35 | } 36 | }); 37 | } 38 | }); 39 | 40 | // Function takes 2 arguments - number and country. Looks up the Intl Code from 41 | // the Country Codes JSON and returns a correctly formatted number. 42 | const formatToIntl = (number, country) => { 43 | if (country) { 44 | for (let countryInfo of countryCodes) { 45 | if ( 46 | countryInfo.name.toLowerCase().replace(/\s+/g, '') == 47 | country.toLowerCase().replace(/\s+/g, '') 48 | ) { 49 | number = number.replace('0', countryInfo.dial_code) 50 | console.log(`Country name=${countryInfo.name} dialCode=${countryInfo.dial_code}`); 51 | console.log(`Formatted number: ${number}`); 52 | } 53 | } 54 | } else { 55 | console.log(`Country not found for number=${number} country=${country}`); 56 | } 57 | 58 | return number; 59 | } 60 | 61 | // Countries and Dial Codes Lookup JSON 62 | const countryCodes = [{ 63 | "name": "Afghanistan", 64 | "dial_code": "+93", 65 | "code": "AF" 66 | }, 67 | { 68 | "name": "Aland Islands", 69 | "dial_code": "+358", 70 | "code": "AX" 71 | }, 72 | { 73 | "name": "Albania", 74 | "dial_code": "+355", 75 | "code": "AL" 76 | }, 77 | { 78 | "name": "Algeria", 79 | "dial_code": "+213", 80 | "code": "DZ" 81 | }, 82 | { 83 | "name": "AmericanSamoa", 84 | "dial_code": "+1 684", 85 | "code": "AS" 86 | }, 87 | { 88 | "name": "Andorra", 89 | "dial_code": "+376", 90 | "code": "AD" 91 | }, 92 | { 93 | "name": "Angola", 94 | "dial_code": "+244", 95 | "code": "AO" 96 | }, 97 | { 98 | "name": "Anguilla", 99 | "dial_code": "+1 264", 100 | "code": "AI" 101 | }, 102 | { 103 | "name": "Antarctica", 104 | "dial_code": "+672", 105 | "code": "AQ" 106 | }, 107 | { 108 | "name": "Antigua and Barbuda", 109 | "dial_code": "+1268", 110 | "code": "AG" 111 | }, 112 | { 113 | "name": "Argentina", 114 | "dial_code": "+54", 115 | "code": "AR" 116 | }, 117 | { 118 | "name": "Armenia", 119 | "dial_code": "+374", 120 | "code": "AM" 121 | }, 122 | { 123 | "name": "Aruba", 124 | "dial_code": "+297", 125 | "code": "AW" 126 | }, 127 | { 128 | "name": "Australia", 129 | "dial_code": "+61", 130 | "code": "AU" 131 | }, 132 | { 133 | "name": "Austria", 134 | "dial_code": "+43", 135 | "code": "AT" 136 | }, 137 | { 138 | "name": "Azerbaijan", 139 | "dial_code": "+994", 140 | "code": "AZ" 141 | }, 142 | { 143 | "name": "Bahamas", 144 | "dial_code": "+1 242", 145 | "code": "BS" 146 | }, 147 | { 148 | "name": "Bahrain", 149 | "dial_code": "+973", 150 | "code": "BH" 151 | }, 152 | { 153 | "name": "Bangladesh", 154 | "dial_code": "+880", 155 | "code": "BD" 156 | }, 157 | { 158 | "name": "Barbados", 159 | "dial_code": "+1 246", 160 | "code": "BB" 161 | }, 162 | { 163 | "name": "Belarus", 164 | "dial_code": "+375", 165 | "code": "BY" 166 | }, 167 | { 168 | "name": "Belgium", 169 | "dial_code": "+32", 170 | "code": "BE" 171 | }, 172 | { 173 | "name": "Belize", 174 | "dial_code": "+501", 175 | "code": "BZ" 176 | }, 177 | { 178 | "name": "Benin", 179 | "dial_code": "+229", 180 | "code": "BJ" 181 | }, 182 | { 183 | "name": "Bermuda", 184 | "dial_code": "+1 441", 185 | "code": "BM" 186 | }, 187 | { 188 | "name": "Bhutan", 189 | "dial_code": "+975", 190 | "code": "BT" 191 | }, 192 | { 193 | "name": "Bolivia, Plurinational State of", 194 | "dial_code": "+591", 195 | "code": "BO" 196 | }, 197 | { 198 | "name": "Bosnia and Herzegovina", 199 | "dial_code": "+387", 200 | "code": "BA" 201 | }, 202 | { 203 | "name": "Botswana", 204 | "dial_code": "+267", 205 | "code": "BW" 206 | }, 207 | { 208 | "name": "Brazil", 209 | "dial_code": "+55", 210 | "code": "BR" 211 | }, 212 | { 213 | "name": "British Indian Ocean Territory", 214 | "dial_code": "+246", 215 | "code": "IO" 216 | }, 217 | { 218 | "name": "Brunei Darussalam", 219 | "dial_code": "+673", 220 | "code": "BN" 221 | }, 222 | { 223 | "name": "Bulgaria", 224 | "dial_code": "+359", 225 | "code": "BG" 226 | }, 227 | { 228 | "name": "Burkina Faso", 229 | "dial_code": "+226", 230 | "code": "BF" 231 | }, 232 | { 233 | "name": "Burundi", 234 | "dial_code": "+257", 235 | "code": "BI" 236 | }, 237 | { 238 | "name": "Cambodia", 239 | "dial_code": "+855", 240 | "code": "KH" 241 | }, 242 | { 243 | "name": "Cameroon", 244 | "dial_code": "+237", 245 | "code": "CM" 246 | }, 247 | { 248 | "name": "Canada", 249 | "dial_code": "+1", 250 | "code": "CA" 251 | }, 252 | { 253 | "name": "Cape Verde", 254 | "dial_code": "+238", 255 | "code": "CV" 256 | }, 257 | { 258 | "name": "Cayman Islands", 259 | "dial_code": "+ 345", 260 | "code": "KY" 261 | }, 262 | { 263 | "name": "Central African Republic", 264 | "dial_code": "+236", 265 | "code": "CF" 266 | }, 267 | { 268 | "name": "Chad", 269 | "dial_code": "+235", 270 | "code": "TD" 271 | }, 272 | { 273 | "name": "Chile", 274 | "dial_code": "+56", 275 | "code": "CL" 276 | }, 277 | { 278 | "name": "China", 279 | "dial_code": "+86", 280 | "code": "CN" 281 | }, 282 | { 283 | "name": "Christmas Island", 284 | "dial_code": "+61", 285 | "code": "CX" 286 | }, 287 | { 288 | "name": "Cocos (Keeling) Islands", 289 | "dial_code": "+61", 290 | "code": "CC" 291 | }, 292 | { 293 | "name": "Colombia", 294 | "dial_code": "+57", 295 | "code": "CO" 296 | }, 297 | { 298 | "name": "Comoros", 299 | "dial_code": "+269", 300 | "code": "KM" 301 | }, 302 | { 303 | "name": "Congo", 304 | "dial_code": "+242", 305 | "code": "CG" 306 | }, 307 | { 308 | "name": "Congo, The Democratic Republic of the Congo", 309 | "dial_code": "+243", 310 | "code": "CD" 311 | }, 312 | { 313 | "name": "Cook Islands", 314 | "dial_code": "+682", 315 | "code": "CK" 316 | }, 317 | { 318 | "name": "Costa Rica", 319 | "dial_code": "+506", 320 | "code": "CR" 321 | }, 322 | { 323 | "name": "Cote d'Ivoire", 324 | "dial_code": "+225", 325 | "code": "CI" 326 | }, 327 | { 328 | "name": "Croatia", 329 | "dial_code": "+385", 330 | "code": "HR" 331 | }, 332 | { 333 | "name": "Cuba", 334 | "dial_code": "+53", 335 | "code": "CU" 336 | }, 337 | { 338 | "name": "Cyprus", 339 | "dial_code": "+357", 340 | "code": "CY" 341 | }, 342 | { 343 | "name": "Czech Republic", 344 | "dial_code": "+420", 345 | "code": "CZ" 346 | }, 347 | { 348 | "name": "Denmark", 349 | "dial_code": "+45", 350 | "code": "DK" 351 | }, 352 | { 353 | "name": "Djibouti", 354 | "dial_code": "+253", 355 | "code": "DJ" 356 | }, 357 | { 358 | "name": "Dominica", 359 | "dial_code": "+1 767", 360 | "code": "DM" 361 | }, 362 | { 363 | "name": "Dominican Republic", 364 | "dial_code": "+1 849", 365 | "code": "DO" 366 | }, 367 | { 368 | "name": "Ecuador", 369 | "dial_code": "+593", 370 | "code": "EC" 371 | }, 372 | { 373 | "name": "Egypt", 374 | "dial_code": "+20", 375 | "code": "EG" 376 | }, 377 | { 378 | "name": "El Salvador", 379 | "dial_code": "+503", 380 | "code": "SV" 381 | }, 382 | { 383 | "name": "Equatorial Guinea", 384 | "dial_code": "+240", 385 | "code": "GQ" 386 | }, 387 | { 388 | "name": "Eritrea", 389 | "dial_code": "+291", 390 | "code": "ER" 391 | }, 392 | { 393 | "name": "Estonia", 394 | "dial_code": "+372", 395 | "code": "EE" 396 | }, 397 | { 398 | "name": "Ethiopia", 399 | "dial_code": "+251", 400 | "code": "ET" 401 | }, 402 | { 403 | "name": "Falkland Islands (Malvinas)", 404 | "dial_code": "+500", 405 | "code": "FK" 406 | }, 407 | { 408 | "name": "Faroe Islands", 409 | "dial_code": "+298", 410 | "code": "FO" 411 | }, 412 | { 413 | "name": "Fiji", 414 | "dial_code": "+679", 415 | "code": "FJ" 416 | }, 417 | { 418 | "name": "Finland", 419 | "dial_code": "+358", 420 | "code": "FI" 421 | }, 422 | { 423 | "name": "France", 424 | "dial_code": "+33", 425 | "code": "FR" 426 | }, 427 | { 428 | "name": "French Guiana", 429 | "dial_code": "+594", 430 | "code": "GF" 431 | }, 432 | { 433 | "name": "French Polynesia", 434 | "dial_code": "+689", 435 | "code": "PF" 436 | }, 437 | { 438 | "name": "Gabon", 439 | "dial_code": "+241", 440 | "code": "GA" 441 | }, 442 | { 443 | "name": "Gambia", 444 | "dial_code": "+220", 445 | "code": "GM" 446 | }, 447 | { 448 | "name": "Georgia", 449 | "dial_code": "+995", 450 | "code": "GE" 451 | }, 452 | { 453 | "name": "Germany", 454 | "dial_code": "+49", 455 | "code": "DE" 456 | }, 457 | { 458 | "name": "Ghana", 459 | "dial_code": "+233", 460 | "code": "GH" 461 | }, 462 | { 463 | "name": "Gibraltar", 464 | "dial_code": "+350", 465 | "code": "GI" 466 | }, 467 | { 468 | "name": "Greece", 469 | "dial_code": "+30", 470 | "code": "GR" 471 | }, 472 | { 473 | "name": "Greenland", 474 | "dial_code": "+299", 475 | "code": "GL" 476 | }, 477 | { 478 | "name": "Grenada", 479 | "dial_code": "+1 473", 480 | "code": "GD" 481 | }, 482 | { 483 | "name": "Guadeloupe", 484 | "dial_code": "+590", 485 | "code": "GP" 486 | }, 487 | { 488 | "name": "Guam", 489 | "dial_code": "+1 671", 490 | "code": "GU" 491 | }, 492 | { 493 | "name": "Guatemala", 494 | "dial_code": "+502", 495 | "code": "GT" 496 | }, 497 | { 498 | "name": "Guernsey", 499 | "dial_code": "+44", 500 | "code": "GG" 501 | }, 502 | { 503 | "name": "Guinea", 504 | "dial_code": "+224", 505 | "code": "GN" 506 | }, 507 | { 508 | "name": "Guinea-Bissau", 509 | "dial_code": "+245", 510 | "code": "GW" 511 | }, 512 | { 513 | "name": "Guyana", 514 | "dial_code": "+595", 515 | "code": "GY" 516 | }, 517 | { 518 | "name": "Haiti", 519 | "dial_code": "+509", 520 | "code": "HT" 521 | }, 522 | { 523 | "name": "Holy See (Vatican City State)", 524 | "dial_code": "+379", 525 | "code": "VA" 526 | }, 527 | { 528 | "name": "Honduras", 529 | "dial_code": "+504", 530 | "code": "HN" 531 | }, 532 | { 533 | "name": "Hong Kong", 534 | "dial_code": "+852", 535 | "code": "HK" 536 | }, 537 | { 538 | "name": "Hungary", 539 | "dial_code": "+36", 540 | "code": "HU" 541 | }, 542 | { 543 | "name": "Iceland", 544 | "dial_code": "+354", 545 | "code": "IS" 546 | }, 547 | { 548 | "name": "India", 549 | "dial_code": "+91", 550 | "code": "IN" 551 | }, 552 | { 553 | "name": "Indonesia", 554 | "dial_code": "+62", 555 | "code": "ID" 556 | }, 557 | { 558 | "name": "Iran, Islamic Republic of Persian Gulf", 559 | "dial_code": "+98", 560 | "code": "IR" 561 | }, 562 | { 563 | "name": "Iraq", 564 | "dial_code": "+964", 565 | "code": "IQ" 566 | }, 567 | { 568 | "name": "Ireland", 569 | "dial_code": "+353", 570 | "code": "IE" 571 | }, 572 | { 573 | "name": "Isle of Man", 574 | "dial_code": "+44", 575 | "code": "IM" 576 | }, 577 | { 578 | "name": "Israel", 579 | "dial_code": "+972", 580 | "code": "IL" 581 | }, 582 | { 583 | "name": "Italy", 584 | "dial_code": "+39", 585 | "code": "IT" 586 | }, 587 | { 588 | "name": "Jamaica", 589 | "dial_code": "+1 876", 590 | "code": "JM" 591 | }, 592 | { 593 | "name": "Japan", 594 | "dial_code": "+81", 595 | "code": "JP" 596 | }, 597 | { 598 | "name": "Jersey", 599 | "dial_code": "+44", 600 | "code": "JE" 601 | }, 602 | { 603 | "name": "Jordan", 604 | "dial_code": "+962", 605 | "code": "JO" 606 | }, 607 | { 608 | "name": "Kazakhstan", 609 | "dial_code": "+7 7", 610 | "code": "KZ" 611 | }, 612 | { 613 | "name": "Kenya", 614 | "dial_code": "+254", 615 | "code": "KE" 616 | }, 617 | { 618 | "name": "Kiribati", 619 | "dial_code": "+686", 620 | "code": "KI" 621 | }, 622 | { 623 | "name": "Korea, Democratic People's Republic of Korea", 624 | "dial_code": "+850", 625 | "code": "KP" 626 | }, 627 | { 628 | "name": "Korea, Republic of South Korea", 629 | "dial_code": "+82", 630 | "code": "KR" 631 | }, 632 | { 633 | "name": "Kosovo", 634 | "dial_code": "+383", 635 | "code": "XK" 636 | }, 637 | { 638 | "name": "Kuwait", 639 | "dial_code": "+965", 640 | "code": "KW" 641 | }, 642 | { 643 | "name": "Kyrgyzstan", 644 | "dial_code": "+996", 645 | "code": "KG" 646 | }, 647 | { 648 | "name": "Laos", 649 | "dial_code": "+856", 650 | "code": "LA" 651 | }, 652 | { 653 | "name": "Latvia", 654 | "dial_code": "+371", 655 | "code": "LV" 656 | }, 657 | { 658 | "name": "Lebanon", 659 | "dial_code": "+961", 660 | "code": "LB" 661 | }, 662 | { 663 | "name": "Lesotho", 664 | "dial_code": "+266", 665 | "code": "LS" 666 | }, 667 | { 668 | "name": "Liberia", 669 | "dial_code": "+231", 670 | "code": "LR" 671 | }, 672 | { 673 | "name": "Libyan Arab Jamahiriya", 674 | "dial_code": "+218", 675 | "code": "LY" 676 | }, 677 | { 678 | "name": "Liechtenstein", 679 | "dial_code": "+423", 680 | "code": "LI" 681 | }, 682 | { 683 | "name": "Lithuania", 684 | "dial_code": "+370", 685 | "code": "LT" 686 | }, 687 | { 688 | "name": "Luxembourg", 689 | "dial_code": "+352", 690 | "code": "LU" 691 | }, 692 | { 693 | "name": "Macao", 694 | "dial_code": "+853", 695 | "code": "MO" 696 | }, 697 | { 698 | "name": "Macedonia", 699 | "dial_code": "+389", 700 | "code": "MK" 701 | }, 702 | { 703 | "name": "Madagascar", 704 | "dial_code": "+261", 705 | "code": "MG" 706 | }, 707 | { 708 | "name": "Malawi", 709 | "dial_code": "+265", 710 | "code": "MW" 711 | }, 712 | { 713 | "name": "Malaysia", 714 | "dial_code": "+60", 715 | "code": "MY" 716 | }, 717 | { 718 | "name": "Maldives", 719 | "dial_code": "+960", 720 | "code": "MV" 721 | }, 722 | { 723 | "name": "Mali", 724 | "dial_code": "+223", 725 | "code": "ML" 726 | }, 727 | { 728 | "name": "Malta", 729 | "dial_code": "+356", 730 | "code": "MT" 731 | }, 732 | { 733 | "name": "Marshall Islands", 734 | "dial_code": "+692", 735 | "code": "MH" 736 | }, 737 | { 738 | "name": "Martinique", 739 | "dial_code": "+596", 740 | "code": "MQ" 741 | }, 742 | { 743 | "name": "Mauritania", 744 | "dial_code": "+222", 745 | "code": "MR" 746 | }, 747 | { 748 | "name": "Mauritius", 749 | "dial_code": "+230", 750 | "code": "MU" 751 | }, 752 | { 753 | "name": "Mayotte", 754 | "dial_code": "+262", 755 | "code": "YT" 756 | }, 757 | { 758 | "name": "Mexico", 759 | "dial_code": "+52", 760 | "code": "MX" 761 | }, 762 | { 763 | "name": "Micronesia, Federated States of Micronesia", 764 | "dial_code": "+691", 765 | "code": "FM" 766 | }, 767 | { 768 | "name": "Moldova", 769 | "dial_code": "+373", 770 | "code": "MD" 771 | }, 772 | { 773 | "name": "Monaco", 774 | "dial_code": "+377", 775 | "code": "MC" 776 | }, 777 | { 778 | "name": "Mongolia", 779 | "dial_code": "+976", 780 | "code": "MN" 781 | }, 782 | { 783 | "name": "Montenegro", 784 | "dial_code": "+382", 785 | "code": "ME" 786 | }, 787 | { 788 | "name": "Montserrat", 789 | "dial_code": "+1664", 790 | "code": "MS" 791 | }, 792 | { 793 | "name": "Morocco", 794 | "dial_code": "+212", 795 | "code": "MA" 796 | }, 797 | { 798 | "name": "Mozambique", 799 | "dial_code": "+258", 800 | "code": "MZ" 801 | }, 802 | { 803 | "name": "Myanmar", 804 | "dial_code": "+95", 805 | "code": "MM" 806 | }, 807 | { 808 | "name": "Namibia", 809 | "dial_code": "+264", 810 | "code": "NA" 811 | }, 812 | { 813 | "name": "Nauru", 814 | "dial_code": "+674", 815 | "code": "NR" 816 | }, 817 | { 818 | "name": "Nepal", 819 | "dial_code": "+977", 820 | "code": "NP" 821 | }, 822 | { 823 | "name": "Netherlands", 824 | "dial_code": "+31", 825 | "code": "NL" 826 | }, 827 | { 828 | "name": "Netherlands Antilles", 829 | "dial_code": "+599", 830 | "code": "AN" 831 | }, 832 | { 833 | "name": "New Caledonia", 834 | "dial_code": "+687", 835 | "code": "NC" 836 | }, 837 | { 838 | "name": "New Zealand", 839 | "dial_code": "+64", 840 | "code": "NZ" 841 | }, 842 | { 843 | "name": "Nicaragua", 844 | "dial_code": "+505", 845 | "code": "NI" 846 | }, 847 | { 848 | "name": "Niger", 849 | "dial_code": "+227", 850 | "code": "NE" 851 | }, 852 | { 853 | "name": "Nigeria", 854 | "dial_code": "+234", 855 | "code": "NG" 856 | }, 857 | { 858 | "name": "Niue", 859 | "dial_code": "+683", 860 | "code": "NU" 861 | }, 862 | { 863 | "name": "Norfolk Island", 864 | "dial_code": "+672", 865 | "code": "NF" 866 | }, 867 | { 868 | "name": "Northern Mariana Islands", 869 | "dial_code": "+1 670", 870 | "code": "MP" 871 | }, 872 | { 873 | "name": "Norway", 874 | "dial_code": "+47", 875 | "code": "NO" 876 | }, 877 | { 878 | "name": "Oman", 879 | "dial_code": "+968", 880 | "code": "OM" 881 | }, 882 | { 883 | "name": "Pakistan", 884 | "dial_code": "+92", 885 | "code": "PK" 886 | }, 887 | { 888 | "name": "Palau", 889 | "dial_code": "+680", 890 | "code": "PW" 891 | }, 892 | { 893 | "name": "Palestinian Territory, Occupied", 894 | "dial_code": "+970", 895 | "code": "PS" 896 | }, 897 | { 898 | "name": "Panama", 899 | "dial_code": "+507", 900 | "code": "PA" 901 | }, 902 | { 903 | "name": "Papua New Guinea", 904 | "dial_code": "+675", 905 | "code": "PG" 906 | }, 907 | { 908 | "name": "Paraguay", 909 | "dial_code": "+595", 910 | "code": "PY" 911 | }, 912 | { 913 | "name": "Peru", 914 | "dial_code": "+51", 915 | "code": "PE" 916 | }, 917 | { 918 | "name": "Philippines", 919 | "dial_code": "+63", 920 | "code": "PH" 921 | }, 922 | { 923 | "name": "Pitcairn", 924 | "dial_code": "+872", 925 | "code": "PN" 926 | }, 927 | { 928 | "name": "Poland", 929 | "dial_code": "+48", 930 | "code": "PL" 931 | }, 932 | { 933 | "name": "Portugal", 934 | "dial_code": "+351", 935 | "code": "PT" 936 | }, 937 | { 938 | "name": "Puerto Rico", 939 | "dial_code": "+1 939", 940 | "code": "PR" 941 | }, 942 | { 943 | "name": "Qatar", 944 | "dial_code": "+974", 945 | "code": "QA" 946 | }, 947 | { 948 | "name": "Romania", 949 | "dial_code": "+40", 950 | "code": "RO" 951 | }, 952 | { 953 | "name": "Russia", 954 | "dial_code": "+7", 955 | "code": "RU" 956 | }, 957 | { 958 | "name": "Rwanda", 959 | "dial_code": "+250", 960 | "code": "RW" 961 | }, 962 | { 963 | "name": "Reunion", 964 | "dial_code": "+262", 965 | "code": "RE" 966 | }, 967 | { 968 | "name": "Saint Barthelemy", 969 | "dial_code": "+590", 970 | "code": "BL" 971 | }, 972 | { 973 | "name": "Saint Helena, Ascension and Tristan Da Cunha", 974 | "dial_code": "+290", 975 | "code": "SH" 976 | }, 977 | { 978 | "name": "Saint Kitts and Nevis", 979 | "dial_code": "+1 869", 980 | "code": "KN" 981 | }, 982 | { 983 | "name": "Saint Lucia", 984 | "dial_code": "+1 758", 985 | "code": "LC" 986 | }, 987 | { 988 | "name": "Saint Martin", 989 | "dial_code": "+590", 990 | "code": "MF" 991 | }, 992 | { 993 | "name": "Saint Pierre and Miquelon", 994 | "dial_code": "+508", 995 | "code": "PM" 996 | }, 997 | { 998 | "name": "Saint Vincent and the Grenadines", 999 | "dial_code": "+1 784", 1000 | "code": "VC" 1001 | }, 1002 | { 1003 | "name": "Samoa", 1004 | "dial_code": "+685", 1005 | "code": "WS" 1006 | }, 1007 | { 1008 | "name": "San Marino", 1009 | "dial_code": "+378", 1010 | "code": "SM" 1011 | }, 1012 | { 1013 | "name": "Sao Tome and Principe", 1014 | "dial_code": "+239", 1015 | "code": "ST" 1016 | }, 1017 | { 1018 | "name": "Saudi Arabia", 1019 | "dial_code": "+966", 1020 | "code": "SA" 1021 | }, 1022 | { 1023 | "name": "Senegal", 1024 | "dial_code": "+221", 1025 | "code": "SN" 1026 | }, 1027 | { 1028 | "name": "Serbia", 1029 | "dial_code": "+381", 1030 | "code": "RS" 1031 | }, 1032 | { 1033 | "name": "Seychelles", 1034 | "dial_code": "+248", 1035 | "code": "SC" 1036 | }, 1037 | { 1038 | "name": "Sierra Leone", 1039 | "dial_code": "+232", 1040 | "code": "SL" 1041 | }, 1042 | { 1043 | "name": "Singapore", 1044 | "dial_code": "+65", 1045 | "code": "SG" 1046 | }, 1047 | { 1048 | "name": "Slovakia", 1049 | "dial_code": "+421", 1050 | "code": "SK" 1051 | }, 1052 | { 1053 | "name": "Slovenia", 1054 | "dial_code": "+386", 1055 | "code": "SI" 1056 | }, 1057 | { 1058 | "name": "Solomon Islands", 1059 | "dial_code": "+677", 1060 | "code": "SB" 1061 | }, 1062 | { 1063 | "name": "Somalia", 1064 | "dial_code": "+252", 1065 | "code": "SO" 1066 | }, 1067 | { 1068 | "name": "South Africa", 1069 | "dial_code": "+27", 1070 | "code": "ZA" 1071 | }, 1072 | { 1073 | "name": "South Georgia and the South Sandwich Islands", 1074 | "dial_code": "+500", 1075 | "code": "GS" 1076 | }, 1077 | { 1078 | "name": "Spain", 1079 | "dial_code": "+34", 1080 | "code": "ES" 1081 | }, 1082 | { 1083 | "name": "Sri Lanka", 1084 | "dial_code": "+94", 1085 | "code": "LK" 1086 | }, 1087 | { 1088 | "name": "Sudan", 1089 | "dial_code": "+249", 1090 | "code": "SD" 1091 | }, 1092 | { 1093 | "name": "Suriname", 1094 | "dial_code": "+597", 1095 | "code": "SR" 1096 | }, 1097 | { 1098 | "name": "Svalbard and Jan Mayen", 1099 | "dial_code": "+47", 1100 | "code": "SJ" 1101 | }, 1102 | { 1103 | "name": "Swaziland", 1104 | "dial_code": "+268", 1105 | "code": "SZ" 1106 | }, 1107 | { 1108 | "name": "Sweden", 1109 | "dial_code": "+46", 1110 | "code": "SE" 1111 | }, 1112 | { 1113 | "name": "Switzerland", 1114 | "dial_code": "+41", 1115 | "code": "CH" 1116 | }, 1117 | { 1118 | "name": "Syrian Arab Republic", 1119 | "dial_code": "+963", 1120 | "code": "SY" 1121 | }, 1122 | { 1123 | "name": "Taiwan", 1124 | "dial_code": "+886", 1125 | "code": "TW" 1126 | }, 1127 | { 1128 | "name": "Tajikistan", 1129 | "dial_code": "+992", 1130 | "code": "TJ" 1131 | }, 1132 | { 1133 | "name": "Tanzania, United Republic of Tanzania", 1134 | "dial_code": "+255", 1135 | "code": "TZ" 1136 | }, 1137 | { 1138 | "name": "Thailand", 1139 | "dial_code": "+66", 1140 | "code": "TH" 1141 | }, 1142 | { 1143 | "name": "Timor-Leste", 1144 | "dial_code": "+670", 1145 | "code": "TL" 1146 | }, 1147 | { 1148 | "name": "Togo", 1149 | "dial_code": "+228", 1150 | "code": "TG" 1151 | }, 1152 | { 1153 | "name": "Tokelau", 1154 | "dial_code": "+690", 1155 | "code": "TK" 1156 | }, 1157 | { 1158 | "name": "Tonga", 1159 | "dial_code": "+676", 1160 | "code": "TO" 1161 | }, 1162 | { 1163 | "name": "Trinidad and Tobago", 1164 | "dial_code": "+1 868", 1165 | "code": "TT" 1166 | }, 1167 | { 1168 | "name": "Tunisia", 1169 | "dial_code": "+216", 1170 | "code": "TN" 1171 | }, 1172 | { 1173 | "name": "Turkey", 1174 | "dial_code": "+90", 1175 | "code": "TR" 1176 | }, 1177 | { 1178 | "name": "Turkmenistan", 1179 | "dial_code": "+993", 1180 | "code": "TM" 1181 | }, 1182 | { 1183 | "name": "Turks and Caicos Islands", 1184 | "dial_code": "+1 649", 1185 | "code": "TC" 1186 | }, 1187 | { 1188 | "name": "Tuvalu", 1189 | "dial_code": "+688", 1190 | "code": "TV" 1191 | }, 1192 | { 1193 | "name": "Uganda", 1194 | "dial_code": "+256", 1195 | "code": "UG" 1196 | }, 1197 | { 1198 | "name": "Ukraine", 1199 | "dial_code": "+380", 1200 | "code": "UA" 1201 | }, 1202 | { 1203 | "name": "United Arab Emirates", 1204 | "dial_code": "+971", 1205 | "code": "AE" 1206 | }, 1207 | { 1208 | "name": "United Kingdom", 1209 | "dial_code": "+44", 1210 | "code": "GB" 1211 | }, 1212 | { 1213 | "name": "United States", 1214 | "dial_code": "+1", 1215 | "code": "US" 1216 | }, 1217 | { 1218 | "name": "Uruguay", 1219 | "dial_code": "+598", 1220 | "code": "UY" 1221 | }, 1222 | { 1223 | "name": "Uzbekistan", 1224 | "dial_code": "+998", 1225 | "code": "UZ" 1226 | }, 1227 | { 1228 | "name": "Vanuatu", 1229 | "dial_code": "+678", 1230 | "code": "VU" 1231 | }, 1232 | { 1233 | "name": "Venezuela, Bolivarian Republic of Venezuela", 1234 | "dial_code": "+58", 1235 | "code": "VE" 1236 | }, 1237 | { 1238 | "name": "Vietnam", 1239 | "dial_code": "+84", 1240 | "code": "VN" 1241 | }, 1242 | { 1243 | "name": "Virgin Islands, British", 1244 | "dial_code": "+1 284", 1245 | "code": "VG" 1246 | }, 1247 | { 1248 | "name": "Virgin Islands, U.S.", 1249 | "dial_code": "+1 340", 1250 | "code": "VI" 1251 | }, 1252 | { 1253 | "name": "Wallis and Futuna", 1254 | "dial_code": "+681", 1255 | "code": "WF" 1256 | }, 1257 | { 1258 | "name": "Yemen", 1259 | "dial_code": "+967", 1260 | "code": "YE" 1261 | }, 1262 | { 1263 | "name": "Zambia", 1264 | "dial_code": "+260", 1265 | "code": "ZM" 1266 | }, 1267 | { 1268 | "name": "Zimbabwe", 1269 | "dial_code": "+263", 1270 | "code": "ZW" 1271 | } 1272 | ] 1273 | 1274 | } 1275 | --------------------------------------------------------------------------------