連結Google Business Profile並匯入地點

建立用於連結 Google 的連結,將擁有者的 Google 商家資訊與您的地點配對,確認配對結果,並匯入尚未對應地點的商家資訊。

連結 Google 帳戶後,即可為地點在 Google 上發布內容及取得報表。在連結帳戶之前,地點的進階商家資訊中,Google Maps 那一列會維持 REQUIRING_ACTION,系統不會匯入 Google 評論,貼文無法發布至 GOOGLE,而 Google 分析資料會傳回 null。連結帳戶即可解決這 4 項問題。

批次流程(步驟 1 至 6)只需連結整個 Google 帳戶一次,即可將該帳戶擁有的每筆商家資訊與您的地點進行比對。單一地點流程(步驟 7)每次連結 1 個地點:連結會綁定該地點,因此後續不需要比對步驟。

此連結的適用範圍僅限於擁有該 API 金鑰的帳戶,因此請求主體只包含您的 2 個重新導向 URL。請將擁有者導向傳回的 url;完成後,Google 會將擁有者導回 successUrl 或 errorUrl。

Connect link
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-google \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "successUrl": "https://app.example.com/connect/google/success",    "errorUrl": "https://app.example.com/connect/google/error"  }}'
JSON
{  "data": {    "bulkConnectLinkForGoogle": {      "success": true,      "url": "https://connect.example.com/oauth?token=8f3c1d92-...",      "errors": null    }  }}

上述 url 是預留位置。請將擁有者導向您實際收到的回應中傳回的 url。

連結的有效期限為 24 小時。請檢查 url 是否為非 null 值,不要只依據 success 判斷。如果擁有者的憑證日後到期,請以相同方式建立新連結:這會更新現有連線,而不會建立重複連線。

2. OAuth 導回後列出已連結的帳戶

擁有者抵達您的 successUrl 時,請列出 Google 帳戶。此處採用位移量分頁:records[] 加上 pageInfo.totalRecords,並透過 page 和 perPage 分頁。

List accounts
curl "https://listingsapi.com/api/v4/connected-accounts?publisher=GoogleAccount&page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "connectedAccountsInfo": {      "pageInfo": { "hasNextPage": false, "totalRecords": 1 },      "records": [        {          "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",          "email": "owner@brightsmile-dental.com",          "status": "CONNECTED",          "requestMatchesStatus": "MATCH_COMPLETED",          "connectedLocationsCount": 0        }      ]    }  }}

請選取 email 為剛剛授權的擁有者的那筆記錄,並保留其 connectedAccountId。後續每次呼叫都需要使用這個 UUID。

status含義
CONNECTED憑證有效。可以安全地觸發比對。
MATCH_IN_PROGRESS比對作業已排入佇列或正在執行。
CONNECTIVITY_ISSUE授權已到期。connectivityIssue 會說明原因;請重新產生連結帳戶用的連結。

選用的 status 查詢篩選條件使用不同的字詞(Connected、NotConnected、ConnectionIssue);若將記錄本身的 status 傳回作為篩選值,則會傳回 400。

3. 觸發比對

比對程序會擷取該帳戶可存取的所有商家檔案,並將每個商家檔案與您的其中一個地點配對。此程序只會產生建議,尚未建立任何連結。

Trigger matches
curl -X POST https://listingsapi.com/api/v4/connected-accounts/trigger-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "connectedAccountIds": ["4f712c17-4f95-42dd-90f4-97171a2e67b5"] } }'

回應為 data.connectedAccountsTriggerMatches,並包含 success 和 failedIds。success: true 表示作業已排入佇列;任何無法排入佇列的帳戶都會列在 failedIds 中。

4. 輪詢直到 MATCH_COMPLETED

請使用設有上限的迴圈,每 15 秒輪詢一次帳戶詳細資料。此間隔也能讓 Launch 方案的請求量維持在每分鐘 10 次的限制內。

Poll details
curl https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/details \-H "Authorization: API $LISTINGSAPI_KEY"

記錄會在 data.connectedAccountDetails.details 中傳回,且欄位與清單中的記錄相同。作業執行期間,requestMatchesStatus 為 MATCH_IN_PROGRESS;建議準備就緒時則為 MATCH_COMPLETED。Completed 表示比對程序已完成,不代表已建立任何連結:在您確認之前,connectedLocationsCount 會維持 0。

5. 檢視並確認建議

每項建議會將您的其中一個地點(locationInfo,其 databaseId 為 locationId)與一筆 Google 商家資訊(suggestedLocationInfo)配對。請將雙方資料顯示給操作人員,或在名稱和城市一致時自動接受。

Suggestions
curl "https://listingsapi.com/api/v4/connected-accounts/4f712c17-4f95-42dd-90f4-97171a2e67b5/connection-suggestions?page=1&perPage=20" \-H "Authorization: API $LISTINGSAPI_KEY"
JSON
{  "data": {    "connectionSuggestionsForAccount": {      "pageInfo": { "hasNextPage": false, "totalRecords": 2 },      "records": [        {          "accountType": "GMB",          "matchedDataDatabaseId": "0d5e306f-0da0-46e3-929f-fd3c67ccd8cc",          "locationId": "73415",          "locationInfo": { "name": "My Business", "city": "Lincoln", "stateIso": "NE" },          "suggestedLocationInfo": { "locationInfo": { "name": "My Business", "city": "Lincoln" } }        }      ]    }  }}

建議沒有 id。若要確認建議,您需要建立比對記錄 id:將 <MatchRecordType>:<matchedDataDatabaseId> 以 base64 編碼,其中的類型從 accountType 中選取。對於 Google(GMB),類型為 GmbLocationMatchedData,因此上述記錄編碼後為 R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M=。Facebook(FB)使用 FbLocationMatchedData,Apple(APPLE)則使用 AppleLocationMatchedData。

Confirm matches
# base64 of GmbLocationMatchedData:0d5e306f-0da0-46e3-929f-fd3c67ccd8cccurl -X POST https://listingsapi.com/api/v4/connected-accounts/confirm-matches \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "input": { "matchRecordIds": ["R21iTG9jYXRpb25NYXRjaGVkRGF0YTowZDVlMzA2Zi0wZGEwLTQ2ZTMtOTI5Zi1mZDNjNjdjY2Q4Y2M="] } }'

回應為 data.confirmConnectMatches,並包含 success 和 failedIds。failedIds 包含無法確認的已解碼 matchedDataDatabaseId 值,例如記錄已使用過,或地點在比對後遭到封存。使用錯誤類型建立的 id 不會導致呼叫傳回 400:該呼叫會傳回 200,其中包含 success: false,但沒有 failedIds,因此每次都要檢查 success。未送出的建議會保持原狀。

6. 匯入尚未對應地點的商家資訊

比對只會將商家資訊與您現有的地點配對。若要匯入其餘商家資訊,請先列出該帳戶可查看的所有商家資訊,再從您想匯入的每筆商家資訊建立地點。

Account listings
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connected-account-listings \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{ "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5", "perPage": 50 }'
JSON
{  "data": {    "connectedAccountListings": {      "pageInfo": { "hasNextPage": false, "totalRecords": 60 },      "records": [        {          "id": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA==",          "locationName": "Trial",          "address": "123 William St, NY 10038, Manhattan, US",          "liveLink": null        }      ]    }  }}

傳入 locationInfo 並指定搜尋字詞,即可依名稱、地址、電話、郵遞區號或商店代碼,縮小大型帳戶的搜尋範圍。每筆記錄上的 id 就是接下來 2 次呼叫所接受的 connectedAccountListingId。這 2 個 SDK 都尚未封裝 create-location-from-listing,因此 Python 和 Node 範例會使用相同的標頭直接呼叫 REST。

Create from listing
curl -X POST https://listingsapi.com/api/v4/connected-accounts/create-location-from-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",    "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA=="  }}'
JSON
{  "data": {    "createLocationFromConnectedAccountListing": { "success": true, "locationId": 88277, "errors": null }  }}

系統會根據 Google 資料(名稱、地址、營業時間、類別、電話、屬性)建立地點,並立即將地點連結至該商家資訊。Google 商家資訊會維持在 IN_PROGRESS,直到您的首次地點更新發布至該商家資訊。locationId 就是一般的 databaseId。失敗時,success 為 false,而 errors 會包含 SY81067 之類的代碼(無法透過商家資訊 id 找到對應資料)。欄位名稱是 connectedAccountListingId;結構定義不接受 listingId。

如果商家資訊屬於您現有的地點,請將其連結至該地點,避免建立重複的地點。

Link a listing
curl -X POST https://listingsapi.com/api/v4/connected-accounts/connect-listing \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "connectedAccountId": "4f712c17-4f95-42dd-90f4-97171a2e67b5",    "connectedAccountListingId": "R21iQnVsa0RhdGFMYWtlOjA2MDQ4NmExLTA0NzQtNDc4NS1iMjNhLTBkOGM4MzhmNGQ5OA=="  }}'

連結失敗時會傳回 success: false,並包含 message,例如 Listing record not found,或註明該商家資訊已連結至另一個地點。

7. 改為連結單一地點

如果您已知道擁有者要連結哪個地點,可以略過比對,直接建立與該地點綁定的連結。成功後,擁有者會返回您的 successUrl,且憑證只會附加至該地點。

Single-location link
curl -X POST https://listingsapi.com/api/v4/locations/oauth_connect_url \-H "Authorization: API $LISTINGSAPI_KEY" \-H "Content-Type: application/json" \-d '{  "input": {    "locationId": "TG9jYXRpb246MTgwMDI4OQ==",    "site": "GOOGLE",    "successUrl": "https://app.example.com/locations/1800289/connected",    "errorUrl": "https://app.example.com/locations/1800289/connect-failed"  }}'

回應為 data.createConnectUrl,並包含 success、url 和 errors。此連結只能使用 1 次,因此每次嘗試都應建立新連結。若重新導向 URL 是相對網址或無法解析,系統會傳回 success: true,並包含值為 url 的 null,因此請檢查 url。若金鑰無權存取此地點,則會收到 403,並包含 SY90003。

8. 後續流程

商家資訊連結後,地點的進階商家資訊中,Google Maps 那一列會從 REQUIRING_ACTION 轉為 IN_PROGRESS,最後進入 SYNCED,並附有 listingUrl。系統會自動將 Google 新增為評論來源,因此評論會開始出現在互動清單中,而您對地點所做的更新也會同步至商家檔案。

在儀表板中設定 webhook URL 後,您就不需要輪詢。每個酬載都包含 location_id 和 data.connected_account_id。

事件觸發時機應採取的動作
connection.location_connected商家資訊已連結至地點;data.platform 為 google。在您的 UI 中將該地點標示為已連結。
connection.reauth_required該帳戶的憑證已失效。重新授權前,所有資料都不會同步。建立新連結(步驟 1),並請擁有者重新登入。
connection.listing_inaccessible憑證仍然有效,但對該筆商家資訊的存取權已遭移除。請擁有者在 Google 上恢復存取權,或連結另一筆商家資訊。

後續步驟