Connected accounts
Connect Google and Facebook accounts, match their listings to locations, and disconnect them with the listingsapi-js SDK.
A connected account is an OAuth grant from a Google Business Profile or
Facebook Pages owner. Once the grant exists, the platform can read the
listings that account owns and link each one to a location. All
connected-account methods live directly on the ListingsAPI client instance.
The usual order is: mint a connect link, wait for the owner to authorize, list
the accounts to pick up a connectedAccountId, trigger matching, review the
suggestions, then confirm them or link a listing by hand. Every method
resolves to the payload under its own response key, which each section below
names.
connectGoogleAccount
Mint an account-wide OAuth link for Google Business Profile. Send the owner to
the returned url; they land back on your successUrl or errorUrl when the
consent screen closes.
const link = await client.connectGoogleAccount('https://app.example.com/onboarding/google/done','https://app.example.com/onboarding/google/failed',); console.log(link.url);Parameters
Resolves to the bulkConnectLinkForGoogle payload: url, success, and
errors. Check url and not only success, because an unusable redirect can
come back as a success with a null url. The link is short-lived, so mint a
fresh one per attempt.
connectFacebookAccount
The Facebook Pages counterpart to connectGoogleAccount, with the same two
redirect arguments.
const link = await client.connectFacebookAccount('https://app.example.com/onboarding/facebook/done','https://app.example.com/onboarding/facebook/failed',); console.log(link.url);Parameters
Resolves to the bulkConnectLinkForFacebook payload: url, success, and
errors.
getOauthConnectUrl
Mint an OAuth link scoped to one location instead of a whole account. Use it when you already know which location the owner is connecting and want them back on that location's screen in your own product.
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);Parameters
Resolves to the createConnectUrl payload: url, success, and errors.
Both redirects are required and must be absolute. The link is single-use and
tied to the location you named.
fetchConnectedAccounts
List the Google and Facebook accounts authorized on your account, with
connection health and linked-location counts. This is where you get the
connectedAccountId every later method needs.
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);}Options
Resolves to the connectedAccountsInfo payload: a records array plus
pageInfo with hasNextPage, hasPreviousPage, totalPages, and
totalRecords. Each record carries connectedAccountId, email,
connectedAccountType, connectedLocationsCount, connectivityIssue,
status, and requestMatchesStatus.
The filter and the field do not share a vocabulary. The status option takes
'Connected', 'NotConnected', or 'ConnectionIssue', while a record's own
status reads CONNECTED, MATCH_IN_PROGRESS, or CONNECTIVITY_ISSUE.
Feeding a record's value back in as the filter is rejected.
fetchConnectedAccountDetails
Read one account by its ID. Cheaper than paging the whole list when you already hold the ID, and the method to poll while matching runs.
const details = await client.fetchConnectedAccountDetails('4f712c17-4f95-42dd-90f4-97171a2e67b5',); console.log(details.details.requestMatchesStatus); // MATCH_IN_PROGRESS, then MATCH_COMPLETEDParameters
Resolves to the connectedAccountDetails payload, which nests the record
under details with the same fields the list returns. Watch
requestMatchesStatus to know when suggestions are ready, and read status
plus connectivityIssue to decide whether to prompt for a re-authorization.
fetchConnectedAccountFolders
List the Google account groups (folders) the connected account can reach.
Call it before createGmbListing when the owner wants the new listing in a
specific group.
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' },);Options
Resolves to the getFoldersUnderGoogleAccount array, defaulting to an empty
array. Each folder carries folderId (an accounts/<id> string you pass
through verbatim), folderName, and locationCount, which can be null when
Google reports no count. The path is valid only for Google-type accounts.
fetchConnectedAccountListings
List the publisher listings the connected account has access to. Each
record's id is exactly the connectedAccountListingId that connectListing
takes, so this is how you build a listing picker.
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);}Options
Resolves to the connectedAccountListings payload: records plus pageInfo.
Each record carries id, locationName, address, phone, liveLink, and
accountTypeName. Despite being a write-shaped request underneath, this is a
read: the SDK puts the account ID and paging in the request body for you,
which is what the endpoint requires.
triggerConnectedAccountMatches
Queue the job that pulls every profile the given accounts can reach and matches them against your locations. Call it right after an owner finishes the consent screen, and again after you bulk-create locations.
const result = await client.triggerConnectedAccountMatches(['4f712c17-4f95-42dd-90f4-97171a2e67b5',]); console.log(result.success, result.failedIds);Parameters
Resolves to the connectedAccountsTriggerMatches payload: success and
failedIds. A success of true means the job was queued, not that matching
finished. Poll fetchConnectedAccountDetails until requestMatchesStatus
reads MATCH_COMPLETED, then read the suggestions.
fetchConnectionSuggestions
Read the pairings matching produced, so an operator can compare each location against the publisher listing before anything is linked.
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);}Options
Resolves to the connectionSuggestionsForAccount payload: records plus
pageInfo. A suggestion record has no id field. Its identifier is
matchedDataDatabaseId, and accountType (GMB, FB, or APPLE) says which
kind of match record it is. Keep the two together, because
confirmConnectedAccountMatches needs both to build the ID it takes. An
account with no matches yet returns an empty records array.
confirmConnectedAccountMatches
Accept the reviewed pairings. Confirming links each location to its publisher listing so later location updates flow through.
Build each ID from a suggestion record: join the match-record type name and
the record's matchedDataDatabaseId with a colon, then base64-encode it.
GMB maps to GmbLocationMatchedData, FB to FbLocationMatchedData, and
APPLE to AppleLocationMatchedData.
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);Parameters
Resolves to the confirmConnectMatches payload: success and failedIds.
Check success on every call: an ID that does not decode to one of the three
types comes back as a success-shaped response with success false and no
failedIds. When some IDs fail, failedIds holds the decoded database IDs,
not the encoded values you sent, so match them back against
matchedDataDatabaseId.
Linking one listing to one location
Three methods finish the link between a single matched listing and a single location, and they are documented with the rest of the listing surface rather than twice:
- connectListing attaches a matched listing to a location.
- disconnectListing detaches it again.
- createGmbListing creates a new Google Business Profile for a location that has none, inside a folder from fetchConnectedAccountFolders.
Reach for these after confirmConnectedAccountMatches has handled the bulk of
an account, when one storefront still needs pairing by hand.
oauthDisconnect
The same single-location disconnect, reached through the per-location OAuth path. It takes the identical arguments and resolves to the identical payload, so pick whichever name reads better in your code.
await client.oauthDisconnect(1800289, 'GOOGLE');Parameters
Resolves to the disconnectConnectedAccountsLocations payload, the same key
disconnectListing returns, because both paths run one shared mutation.
disconnectGoogleAccount
Tear down a whole Google account: every location linked through it stops receiving updates. Use it when a customer churns or transfers ownership.
await client.disconnectGoogleAccount('4f712c17-4f95-42dd-90f4-97171a2e67b5');Parameters
Resolves to the gmbBulkDisconnect payload, which carries success. This is
all or nothing: to unlink a single location while keeping the account, use
disconnectListing. A success of true is returned even for an unknown ID,
so read fetchConnectedAccounts afterwards if you need certainty.
disconnectFacebookAccount
The Facebook counterpart, with the same argument and the same all-or-nothing behaviour.
await client.disconnectFacebookAccount('9d2b5a41-0c7e-4f83-bb16-2ac43e70d915');Parameters
Resolves to the fbBulkDisconnect payload, which carries success. The same
caveat applies: an unknown ID still reports success, so verify with
fetchConnectedAccounts.
Typical onboarding workflow
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');Related resources
- Listings, the same connect and disconnect methods in listing context
- Locations, look up location IDs
- Error handling, typed errors thrown by every method
- Connected accounts endpoints, the REST walkthrough behind these methods
- Connect Google and import locations, the full onboarding guide
- Connect Facebook pages, the Facebook variant
- Create a Google Business Profile, the
createGmbListingguide - REST reference: list accounts, account details, folders, account listings, trigger matches, connection suggestions, confirm matches, connect listing, disconnect listing