연결된 계정

listingsapi-js SDK로 Google, Facebook, Apple Business Connect 계정을 연결하고, 각 계정의 등록 정보를 사업장과 매칭하고, 계정 연결을 해제합니다.

연결된 계정은 Google Business Profile, Facebook Pages 또는 Apple Business Connect 소유자가 OAuth로 부여한 접근 권한입니다. 권한이 부여되면 플랫폼은 해당 계정이 소유한 등록 정보를 읽고 각 등록 정보를 사업장에 연결할 수 있습니다. 연결된 계정과 관련된 모든 메서드는 ListingsAPI 클라이언트 인스턴스에 있습니다.

일반적인 순서는 연결 링크를 생성하고, 소유자가 권한을 부여할 때까지 기다린 다음, 계정 목록을 조회하여 connectedAccountId을 얻고, 매칭을 실행하고, 제안을 검토한 후 확정하거나 등록 정보를 수동으로 연결하는 것입니다. 각 메서드는 해당 응답 키 아래의 페이로드를 반환하며, 이 키는 아래 각 섹션에 명시되어 있습니다.

connectGoogleAccount

Google Business Profile에 대한 계정 전체 범위의 OAuth 링크를 생성합니다. 소유자를 반환된 url로 이동시키면 동의 화면이 닫힐 때 지정한 successUrl 또는 errorUrl로 돌아옵니다.

TypeScript
const link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',); console.log(link.url);

매개변수

매개변수유형설명
successUrlstring소유자가 권한을 부여한 후 돌아올 절대 URL입니다.
errorUrlstring소유자가 취소하거나 권한 부여에 실패한 경우 돌아올 절대 URL입니다.

bulkConnectLinkForGoogle 페이로드를 반환하며, 여기에는 url, success, errors이 포함됩니다. 사용할 수 없는 리디렉션이라도 url이 null인 성공 응답으로 반환될 수 있으므로 success뿐 아니라 url도 확인해야 합니다. 링크의 유효 기간이 짧으므로 시도할 때마다 새 링크를 생성합니다.

connectFacebookAccount

connectGoogleAccount에 해당하는 Facebook Pages용 메서드이며, 동일한 리디렉션 인수 2개를 사용합니다.

TypeScript
const link = await client.connectFacebookAccount('https://app.example.com/onboarding/facebook/done','https://app.example.com/onboarding/facebook/failed',); console.log(link.url);

매개변수

매개변수유형설명
successUrlstring소유자가 권한을 부여한 후 돌아올 절대 URL입니다.
errorUrlstring소유자가 취소하거나 권한 부여에 실패한 경우 돌아올 절대 URL입니다.

bulkConnectLinkForFacebook 페이로드를 반환하며, 여기에는 url, success, errors이 포함됩니다.

getOauthConnectUrl

계정 전체가 아닌 사업장 1개로 범위를 제한한 OAuth 링크를 생성합니다. 소유자가 연결할 사업장을 이미 알고 있으며, 연결 후 자체 제품의 해당 사업장 화면으로 돌아오도록 하려는 경우 사용합니다.

TypeScript
const link = await client.getOauthConnectUrl(1800289,'GOOGLE','https://app.example.com/locations/1800289/connected','https://app.example.com/locations/1800289/connect-failed',); console.log(link.url);

매개변수

매개변수유형설명
locationIdstring | number연결이 적용되는 사업장입니다. 숫자 ID는 자동으로 인코딩됩니다.
sitestring연결할 게시 플랫폼으로, 'GOOGLE', 'FACEBOOK' 또는 'APPLE'입니다.
successUrlstring소유자가 권한을 부여한 후 돌아올 절대 URL입니다.
errorUrlstring권한 부여에 실패한 경우 소유자가 돌아올 절대 URL입니다.

createConnectUrl 페이로드를 반환하며, 여기에는 url, success, errors이 포함됩니다. 두 리디렉션 URL은 모두 필수이며 절대 URL이어야 합니다. 링크는 1회만 사용할 수 있으며 지정한 사업장에 연결됩니다.

fetchConnectedAccounts

현재 계정에서 접근 권한을 부여받은 Google 및 Facebook 계정 목록을 연결 상태 및 연결된 사업장 수와 함께 조회합니다. 이후 모든 메서드에 필요한 connectedAccountId을 여기서 얻습니다.

TypeScript
const page1 = await client.fetchConnectedAccounts({publisher: 'GoogleAccount',status: 'Connected',page: 1,perPage: 20,}); for (const record of page1.records ?? []) {console.log(record.email, record.connectedAccountId, record.status);}

옵션

매개변수유형설명
publisherstring계정 유형으로, 예를 들어 'GoogleAccount' 또는 'FacebookAccount'입니다.
statusstring연결 상태 필터로, 'Connected', 'NotConnected' 또는 'ConnectionIssue'입니다.
pagenumber페이지 번호입니다(1부터 시작).
perPagenumber페이지당 레코드 수입니다.

connectedAccountsInfo 페이로드를 반환하며, 여기에는 records 배열과 hasNextPage, hasPreviousPage, totalPages, totalRecords이 포함된 pageInfo가 있습니다. 각 레코드에는 connectedAccountId, email, connectedAccountType, connectedLocationsCount, connectivityIssue, status, requestMatchesStatus이 포함됩니다.

필터와 필드에서 사용하는 값은 서로 다릅니다. status 옵션은 'Connected', 'NotConnected' 또는 'ConnectionIssue'을 받지만, 레코드 자체의 status 값은 CONNECTED, MATCH_IN_PROGRESS 또는 CONNECTIVITY_ISSUE입니다. 레코드의 값을 필터로 다시 입력하면 거부됩니다.

fetchConnectedAccountDetails

ID로 계정 1개를 조회합니다. ID를 이미 알고 있다면 전체 목록을 페이지별로 조회하는 것보다 비용이 적게 들며, 매칭이 실행되는 동안 폴링할 때도 이 메서드를 사용합니다.

TypeScript
const details = await client.fetchConnectedAccountDetails('4f712c17-4f95-42dd-90f4-97171a2e67b5',); console.log(details.details.requestMatchesStatus); // MATCH_IN_PROGRESS, then MATCH_COMPLETED

매개변수

매개변수유형설명
connectedAccountIdstringfetchConnectedAccounts에서 얻은 연결된 계정의 ID입니다.

connectedAccountDetails 페이로드를 반환하며, 레코드는 details 아래에 중첩되어 있고 목록 조회에서 반환하는 것과 동일한 필드를 포함합니다. requestMatchesStatus를 확인하여 제안이 준비되었는지 파악하고, status과 connectivityIssue를 읽어 권한을 다시 부여하도록 요청할지 결정합니다.

fetchConnectedAccountFolders

연결된 계정이 접근할 수 있는 Google 계정 그룹(폴더) 목록을 조회합니다. 소유자가 새 등록 정보를 특정 그룹에 추가하려는 경우 createGmbListing 전에 호출합니다.

TypeScript
const folders = await client.fetchConnectedAccountFolders('4f712c17-4f95-42dd-90f4-97171a2e67b5',); for (const folder of folders) {console.log(folder.folderId, folder.folderName, folder.locationCount);} // Narrow by nameconst matching = await client.fetchConnectedAccountFolders('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ folderName: 'West Region' },);

옵션

매개변수유형설명
folderNamestring결과를 이 이름과 일치하는 폴더로 제한합니다.

getFoldersUnderGoogleAccount 배열을 반환하며, 기본값은 빈 배열입니다. 각 폴더에는 folderId(변경 없이 그대로 전달하는 accounts/<id> 문자열), folderName, locationCount가 포함되며, Google에서 개수를 제공하지 않으면 마지막 값은 null일 수 있습니다. 이 경로는 Google 유형 계정에서만 유효합니다.

fetchConnectedAccountListings

연결된 계정이 접근할 수 있는 게시 플랫폼의 등록 정보 목록을 조회합니다. 각 레코드의 id은 connectListing에서 받는 connectedAccountListingId과 정확히 같으므로, 이를 사용하여 등록 정보 선택기를 구현합니다.

TypeScript
const listings = await client.fetchConnectedAccountListings('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ locationInfo: 'William St', page: 1, perPage: 50 },); for (const record of listings.records ?? []) {console.log(record.id, record.locationName, record.address);}

옵션

매개변수유형설명
locationInfostring업체명, 주소, 지역, 우편번호, 매장 코드 및 전화번호를 대상으로 하는 자유 텍스트 검색입니다.
pagenumber페이지 번호입니다(1부터 시작).
perPagenumber페이지당 레코드 수입니다. 기본값은 500이며, 최댓값도 500입니다.

connectedAccountListings 페이로드를 반환하며, 여기에는 records과 pageInfo가 포함됩니다. 각 레코드에는 id, locationName, address, phone, liveLink, accountTypeName이 포함됩니다. 내부적으로 쓰기 형태의 요청을 사용하지만 읽기 작업이며, SDK가 엔드포인트의 요구 사항에 따라 계정 ID와 페이지 정보를 요청 본문에 자동으로 넣습니다.

triggerConnectedAccountMatches

지정한 계정이 접근할 수 있는 모든 프로필을 가져와 현재 계정의 사업장과 매칭하는 작업을 대기열에 추가합니다. 소유자가 동의 화면에서 절차를 마친 직후 호출하고, 사업장을 일괄로 생성한 후 다시 호출합니다.

TypeScript
const result = await client.triggerConnectedAccountMatches(['4f712c17-4f95-42dd-90f4-97171a2e67b5',]); console.log(result.success, result.failedIds);

매개변수

매개변수유형설명
connectedAccountIdsstring[]매칭을 실행할 계정입니다.

connectedAccountsTriggerMatches 페이로드를 반환하며, 여기에는 success과 failedIds가 포함됩니다. success이 true이면 작업이 대기열에 추가되었다는 뜻이며, 매칭이 완료되었다는 뜻은 아닙니다. requestMatchesStatus 값이 MATCH_COMPLETED이 될 때까지 fetchConnectedAccountDetails를 폴링한 다음 제안을 조회합니다.

fetchConnectionSuggestions

연결을 수행하기 전에 운영자가 각 사업장과 게시 플랫폼의 등록 정보를 비교할 수 있도록 매칭으로 생성된 연결 후보 쌍을 조회합니다.

TypeScript
const suggestions = await client.fetchConnectionSuggestions('4f712c17-4f95-42dd-90f4-97171a2e67b5',{ page: 1, perPage: 50 },); for (const record of suggestions.records ?? []) {console.log(record.accountType, record.matchedDataDatabaseId, record.locationInfo.name);}

옵션

매개변수유형설명
pagenumber페이지 번호입니다(1부터 시작).
perPagenumber페이지당 레코드 수입니다.

connectionSuggestionsForAccount 페이로드를 반환하며, 여기에는 records과 pageInfo가 포함됩니다. 제안 레코드에는 id 필드가 없습니다. 식별자는 matchedDataDatabaseId이며, accountType(GMB, FB 또는 APPLE)는 매칭 레코드의 유형을 나타냅니다. confirmConnectedAccountMatches에서 받는 ID를 구성하려면 두 값이 모두 필요하므로 함께 보관합니다. 아직 매칭 결과가 없는 계정은 빈 records 배열을 반환합니다.

confirmConnectedAccountMatches

검토한 연결 후보 쌍을 승인합니다. 확정하면 각 사업장이 해당 게시 플랫폼의 등록 정보에 연결되어 이후 사업장 업데이트가 반영됩니다.

제안 레코드에서 각 ID를 구성하려면 매칭 레코드 유형 이름과 레코드의 matchedDataDatabaseId을 콜론으로 연결한 다음 base64로 인코딩합니다. GMB은 GmbLocationMatchedData에, FB은 FbLocationMatchedData에, APPLE는 AppleLocationMatchedData에 매핑됩니다.

TypeScript
const TYPES: Record<string, string> = {GMB: 'GmbLocationMatchedData',FB: 'FbLocationMatchedData',APPLE: 'AppleLocationMatchedData',}; const matchRecordId = (record: any) =>Buffer.from(TYPES[record.accountType] + ':' + record.matchedDataDatabaseId).toString('base64'); const approved = (suggestions.records ?? []).map(matchRecordId);const result = await client.confirmConnectedAccountMatches(approved); console.log(result.success, result.failedIds);

매개변수

매개변수유형설명
matchRecordIdsstring[]확정할 인코딩된 매칭 레코드 ID입니다. 최소 1개가 필요합니다.

confirmConnectMatches 페이로드를 반환하며, 여기에는 success과 failedIds가 포함됩니다. 호출할 때마다 success을 확인해야 하며, ID를 디코딩한 결과가 3가지 유형 중 하나가 아니면 success가 false이고 failedIds가 없는 성공 형식의 응답이 반환됩니다. 일부 ID의 처리가 실패하면 failedIds에는 전송한 인코딩 값이 아니라 디코딩된 데이터베이스 ID가 포함되므로, 이를 matchedDataDatabaseId과 대조합니다.

등록 정보 1개를 사업장 1개에 연결하기

다음 3개 메서드는 매칭된 등록 정보 1개와 사업장 1개 사이의 연결을 처리하며, 중복으로 설명하지 않고 다른 등록 정보 엔드포인트와 함께 설명합니다:

confirmConnectedAccountMatches가 계정의 대부분을 처리한 후에도 매장 1개를 수동으로 연결해야 할 때 이 메서드들을 사용합니다.


oauthDisconnect

사업장별 OAuth 경로를 통해 동일한 단일 사업장 연결 해제 작업을 수행합니다. 동일한 인수를 받고 동일한 페이로드로 이행되므로 코드에서 더 읽기 쉬운 이름을 선택합니다.

TypeScript
await client.oauthDisconnect(1800289, 'GOOGLE');

매개변수

매개변수유형설명
locationIdstring | number연결을 해제할 사업장입니다.
sitestring연결을 해제할 게시 플랫폼으로, 'GOOGLE', 'FACEBOOK' 또는 'APPLE'입니다.

disconnectConnectedAccountsLocations 페이로드로 이행되며, 이는 disconnectListing가 반환하는 것과 동일한 키입니다.

disconnectGoogleAccount

Google 계정 전체의 연결을 해제합니다. 해당 계정을 통해 연결된 모든 사업장이 더 이상 업데이트를 받지 않습니다. 고객이 서비스 이용을 중단하거나 소유권을 이전할 때 사용합니다.

TypeScript
await client.disconnectGoogleAccount('4f712c17-4f95-42dd-90f4-97171a2e67b5');

매개변수

매개변수유형설명
connectedAccountIdstring연결을 해제할 Google 계정입니다.

success가 포함된 gmbBulkDisconnect 페이로드로 이행됩니다. 이 작업은 전체에 적용되며 일부에만 적용할 수 없습니다. 계정을 유지하면서 사업장 1개의 연결만 해제하려면 disconnectListing를 사용합니다. 알 수 없는 ID에도 success 값으로 true가 반환되므로 확실히 확인해야 한다면 이후에 fetchConnectedAccounts를 조회합니다.

disconnectFacebookAccount

Facebook에서 같은 작업을 수행하며, 동일한 인수를 받고 마찬가지로 전체에 적용되거나 전혀 적용되지 않습니다.

TypeScript
await client.disconnectFacebookAccount('9d2b5a41-0c7e-4f83-bb16-2ac43e70d915');

매개변수

매개변수유형설명
connectedAccountIdstring연결을 해제할 Facebook 계정입니다.

success가 포함된 fbBulkDisconnect 페이로드로 이행됩니다. 동일한 주의 사항이 적용됩니다. 알 수 없는 ID에도 성공을 반환하므로 fetchConnectedAccounts로 확인합니다.

일반적인 온보딩 워크플로

connect_google.ts
import { ListingsAPI } from 'listingsapi-js'; const client = new ListingsAPI(); // reads LISTINGSAPI_KEY from env // 1. Send the owner through the consent screenconst link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',);console.log('open:', link.url); // 2. After they return, pick up the account IDconst accounts = await client.fetchConnectedAccounts({ publisher: 'GoogleAccount' });const accountId = accounts.records[0].connectedAccountId; // 3. Match its listings against your locationsawait client.triggerConnectedAccountMatches([accountId]); // 4. Poll until matching finisheslet details = await client.fetchConnectedAccountDetails(accountId);while (details.details.requestMatchesStatus !== 'MATCH_COMPLETED') {await new Promise((resolve) => setTimeout(resolve, 5000));details = await client.fetchConnectedAccountDetails(accountId);} // 5. Read the suggestions and confirm the approved onesconst suggestions = await client.fetchConnectionSuggestions(accountId, { perPage: 100 });console.log('found ' + (suggestions.records ?? []).length + ' suggested matches');