├── 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 |
--------------------------------------------------------------------------------