LoginSignup
2
3

More than 1 year has passed since last update.

Hubspot APIでコール記録を取得してみよう

Last updated at Posted at 2022-04-25

Hubspotのコール記録をどうしても抜き出したいんです!と社内で言われたので、
遠い記憶を手繰り寄せながらコードを書いてみました。

今回使用したAPIはHubspot API V3のcallsです。
https://developers.hubspot.com/docs/api/crm/calls

2年ほど前はまだEngagement(コール、ミーティング、コメントなど)はV1しかなかったのですが、
いつの間にかV3がでていました。

そしてV3では「Search」が用意されているため、
簡単にデータを引っ張ってくることができました。

まずは完成品がこちら。

engagement.js
function getEngagement() {
    const spreadsheet = SpreadsheetApp.openById("スプレッドシートのシートIDをいれる");
    const sheetHubspot = spreadsheet.getSheetByName('タブ名');

    //シート全クリア
    sheetHubspot.getRange(2,1,9999,20).clear();

    //HubspotのAPI接続設定
    const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

    //更新する項目(列)数
    const lastColumn = 5;

    //フィルターに使用する日時(UNIXミリ秒)
    const date = 1643641200000; //2022-02-01

    //取得準備
    let ListCalls = [];//リストの配列
    let hasMore = true; //追加データが有る場合はtrue
    let after = 0; //一度で取得できなかった場合、続きから取得

    while(hasMore){
        //1秒経過してから処理開始
        Utilities.sleep(1000);

        //Hubspotからコンタクトを取得
        let url = "https://api.hubapi.com/crm/v3/objects/calls/search" + "?hapikey=" + API_KEY;
        let headers = {"content-type" : "application/json"}
        let payload = {
            "filterGroups": [
                {
                  "filters": [
                    {
                      "propertyName": "hs_timestamp",
                      "operator": "GTE",
                      "value": 1643641200000 //2022-02-01
                    }
                  ]
                }
            ],
            "sorts": ["hs_timestamp"],
            "properties": ["hs_call_body,hs_call_disposition,hs_timestamp,hubspot_owner_id"],
            "limit": 100,
            "after": after
        }
        payload = JSON.stringify(payload);
        let options = {
            "method": 'POST',
            "headers": headers,
            "payload": payload,
            "muteHttpExceptions": true
        };
        let response = UrlFetchApp.fetch(url, options);

        //スプレッドシートにマッピング
        if(response){
            //JSON→配列化
            let responseArr = JSON.parse(response.getContentText());
            
            //連想配列からkeyが"results"の中身を取得
            let arrContacts = responseArr.results;
            
            //連想配列からkeyが"properties"の中身を配列形式で取得
            let arrProperties = arrContacts.map(obj => obj.properties);

            //リストの配列
            for (const elem of arrProperties) {
                //アイテム単位の配列
                let ItemCalls = [];
                
                //A_コール担当者
                if(elem["hubspot_owner_id"]){
                    ItemCalls.push(elem["hubspot_owner_id"]);
                }
                else {
                    ItemCalls.push("");
                }

                //B_コール担当者の名前置換用の列
                ItemCalls.push("");
                
                //C_コール内容
                if(elem["hs_call_body"]){
                    ItemCalls.push(elem["hs_call_body"]);
                }
                else {
                    ItemCalls.push("");
                }

                //D_コール結果
                if(elem["hs_call_disposition"]){
                    ItemCalls.push(elem["hs_call_disposition"]);
                }
                else {
                    ItemCalls.push("");
                }

                //E_コール日
                if(elem["hs_timestamp"]){
                    ItemCalls.push(elem["hs_timestamp"]);
                }
                else {
                    ItemCalls.push("");
                }
                
                //配列に格納
                ListCalls.push(ItemCalls);

            }

            //hasMoreがない場合は終了
            if ("paging" in responseArr) {
                //afterを書き換え
                after = responseArr.paging.next.after;
            }
            else {
                hasMore = false;
            }
        }
        //responseがない場合は終了
        else {
            hasMore = false;
        }
    }

    //シートへの記入
    sheetHubspot.getRange(2, 1, ListCalls.length, lastColumn).setValues(ListCalls);
}

それでは解説をしていきたいと思います。

事前準備

シートの定義

engagement.js
    const spreadsheet = SpreadsheetApp.openById("スプレッドシートのシートIDをいれる");
    const sheetHubspot = spreadsheet.getSheetByName('タブ名');

https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
のxxxxx...に当てはまる部分が「スプレッドシートのシートIDをいれる」に該当します。

そしてタブに任意の名前をつけて、そのタブを更新用のシートにします。

シートの内容クリア

シートクリアは必要に応じて。

engagement.js
    //シート全クリア
    sheetHubspot.getRange(2,1,9999,20).clear();

HubspotのAPI

Hubspotの設定 > 連携 > APIキーに記載があります。

engagement.js
    //HubspotのAPI接続設定
    const API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

更新する列

今回の取得したいプロパティの数を記載しましょう。

engagement.js
    //更新する項目(列)数
    const lastColumn = 5;

フィルターに使用する日時

engagement.js
    //フィルターに使用する日時(UNIXミリ秒)
    const date = 1643641200000; //2022-02-01

Hubspotからの取得

準備

リストにデータを格納し、まとめてスプレッドシートに貼り付ける形式となります。

hasMoreは、後述のwhile内で利用します。
"paging"という言葉が配列の中に格納されている場合はafter(ページ番号)の数字を取得し、存在しない場合はループを終了させる、という役割を果たしています。

engagement.js
    //取得準備
    let ListCalls = [];//リストの配列
    let hasMore = true; //追加データが有る場合はtrue
    let after = 0; //一度で取得できなかった場合、続きから取得
engagement.js
            //hasMoreがない場合は終了
            if ("paging" in responseArr) {
                //afterを書き換え
                after = responseArr.paging.next.after;
            }
            else {
                hasMore = false;
            }

取得

Utilities.sleep(1000);を入れているのは、ループが高速すぎると制限に引っかかるからです。

engagement.js
    while(hasMore){
        //1秒経過してから処理開始
        Utilities.sleep(1000);

フィルターのかけ方ですが、

engagement.js
            "filterGroups": [
                {
                  "filters": [
                    {
                      "propertyName": "hs_timestamp",
                      "operator": "GTE",
                      "value": 1643641200000 //2022-02-01
                    }
                  ]
                }
            ],

公式ドキュメントには以下のように記述があります。

複数のフィルタ条件を含めるには、フィルタを filterGroups でグループ化します。

  • 複数のフィルタが filterGroup 内に存在するときは、AND演算子で結合されます。
  • 複数の filterGroup が含まれる場合、それらはOR演算子を用いて結合されます。

filterGroups は最大3つまで含めることができ、各グループには最大3つのフィルタがあります。たとえば、以下のリクエストは、ファーストネームがAliceで、ラストネームがSmithでない、またはプロパティemailの値を持っているすべてのコンタクトを検索します。

    "filterGroups":[
      {
        "filters":[
          {
            "propertyName": "firstname",
            "operator": "EQ",
            "value": "Alice"
          }
        ]
      }
    ]

すなわち、AかつBにしたいときはfilterGroupsの中にfiltersを並列で配置すればよく、
AまたはBにしたいときはfilterGroupesを並列で配置する、ということですね。

OPERATOR DESCRIPTION
LT 未満(Less than)
LTE 以下(Less than or equal to)
GT より大きい(Greater than)
GTE 以上(Greater than or equal to)
EQ 等しい(Equal to)
NEQ 等しくない(Not equal to)
BETWEEN の間
IN 特定のリストに含まれる
NOT_IN 特定のリストに含まれない
HAS_PROPERTY 既知である
NOT_HAS_PROPERTY 不明である

BetweenのみhighValueが必要になります。

"filterGroups":[{
    "filters":[
        {
    "propertyName":"hs_lastmodifieddate",
     "operator":"BETWEEN",
     "highValue": "1642672800000",
      "value":"1579514400000"
        }
]
}
]

公式ドキュメントはこちら↓

フィルター内に登場する日時の指定は、UNIXミリ秒で指定しています。

"value": 1643641200000 //2022-02-01

UNIXミリ秒は以下のサイトから調べることが可能です。

ざっとポイントを解説してみましたが、わからないことがあればコメントなどで気軽にご質問ください。
必要な情報など追記して、よりわかりやすくしていきたいと思います。

ちなみに前回の記事はこちらです。合わせて参考にしてみてください。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3