diff --git a/README.md b/README.md
index 5b0de09..137753d 100644
--- a/README.md
+++ b/README.md
@@ -113,6 +113,9 @@ long failed = client.posts().stream(PostListParams.create().workspaceId(workspac
| `sequences()` | `list`, `get`, `create`, `update`, `delete`, `enroll`, `unenroll`, `enrollments` |
| `knowledge()` | `list`, `create`, `createText`, `createUrl`, `createFile`, `update`, `delete`, `sync`, `search` |
| `ads()` | `list`, `external`, `boostable`, `connections`, `sources`, `authorizeMeta`, `deleteConnection`, `boost`, `create`, `refresh`, `setStatus`, `delete`, `accountTree`, `createCampaign`, `campaign`, `updateCampaign`, `deleteCampaign`, `duplicateCampaign`, `createAdSet`, `adSet`, `updateAdSet`, `deleteAdSet`, `duplicateAdSet`, `createNetworkAd`, `networkAd`, `updateNetworkAd`, `deleteNetworkAd`, `duplicateNetworkAd`, `bulkSetStatus`, `creatives`, `createCreative`, `creative`, `deleteCreative`, `estimateReach`, `insights`, `adInsights`, `audiences`, `createAudience`, `audience`, `updateAudience`, `deleteAudience`, `addAudienceUsers`, `searchTargeting`, `leadForms`, `createLeadForm`, `leadForm`, `archiveLeadForm`, `leads`, `leadsFeed`, `leadPages`, `subscribeLeadPage`, `unsubscribeLeadPage`, `goals`, `catalogs`, `createCatalog`, `catalog`, `updateCatalog`, `deleteCatalog`, `catalogProducts`, `writeCatalogProducts`, `productFeeds`, `createProductFeed`, `deleteProductFeed`, `feedUploads`, `startFeedUpload`, `productSets`, `createProductSet`, `updateProductSet`, `deleteProductSet`, `reachFrequency`, `createReachFrequency`, `reachFrequencyPrediction`, `reserveReachFrequency`, `cancelReachFrequency`, `library`, `partnershipCreators`, `requestPartnership`, `revokePartnership`, `accountActivity`, `labels`, `createLabel`, `updateLabel`, `deleteLabel`, `applyLabel`, `studies`, `createStudy`, `study`, `deleteStudy`, `iosCampaignLimits`, `highDemandPeriods`, `createHighDemandPeriod`, `deleteHighDemandPeriod`, `valueRuleSets`, `createValueRuleSet`, `deleteValueRuleSet` |
+| `inbox()` | `list`, `threads`, `conversations`, `unreadCount`, `accounts`, `platforms`, `markThreadRead`, `markConversationRead`, `refresh`, `update`, `editComment`, `reply`, `hide`, `unhide`, `delete`, `like`, `unlike`, `pin`, `unpin`, `react`, `startConversation`, `setTyping`, `listApprovals`, `approveReply`, `rejectReply` |
+| `ads()` | `list`, `external`, `boostable`, `connections`, `sources`, `authorizeMeta`, `deleteConnection`, `boost`, `create`, `refresh`, `setStatus`, `delete`, `accountTree`, `createCampaign`, `campaign`, `updateCampaign`, `deleteCampaign`, `duplicateCampaign`, `createAdSet`, `adSet`, `updateAdSet`, `deleteAdSet`, `duplicateAdSet`, `createNetworkAd`, `networkAd`, `updateNetworkAd`, `deleteNetworkAd`, `duplicateNetworkAd`, `bulkSetStatus`, `creatives`, `createCreative`, `creative`, `deleteCreative`, `estimateReach`, `insights`, `adInsights`, `audiences`, `createAudience`, `audience`, `updateAudience`, `deleteAudience`, `addAudienceUsers`, `searchTargeting`, `leadForms`, `createLeadForm`, `leadForm`, `archiveLeadForm`, `leads`, `leadsFeed`, `leadPages`, `subscribeLeadPage`, `unsubscribeLeadPage` |
+| `googleBusiness()` | `getLocation`, `updateLocation`, `getAttributes`, `updateAttributes`, `getMenus`, `replaceMenus`, `getServices`, `replaceServices`, `listMedia`, `addMedia`, `deleteMedia`, `listPlaceActions`, `createPlaceAction`, `updatePlaceAction`, `deletePlaceAction`, `getVerificationOptions`, `startVerification`, `completeVerification`, `getPerformance`, `getSearchKeywords`, `assign` |
| `validate()` | `post`, `length`, `media` |
| `activity()` | `list` |
diff --git a/src/main/java/com/fopost/sdk/FoPost.java b/src/main/java/com/fopost/sdk/FoPost.java
index 3e877f7..b29b11f 100644
--- a/src/main/java/com/fopost/sdk/FoPost.java
+++ b/src/main/java/com/fopost/sdk/FoPost.java
@@ -17,6 +17,7 @@
import com.fopost.sdk.resource.ContactsResource;
import com.fopost.sdk.resource.InboxResource;
import com.fopost.sdk.resource.KnowledgeResource;
+import com.fopost.sdk.resource.GoogleBusinessResource;
import com.fopost.sdk.resource.LabelsResource;
import com.fopost.sdk.resource.MediaResource;
import com.fopost.sdk.resource.PostsResource;
@@ -76,6 +77,7 @@ public final class FoPost {
private final BroadcastsResource broadcasts;
private final SequencesResource sequences;
private final ActivityResource activity;
+ private final GoogleBusinessResource googleBusiness;
private final InboxResource inbox;
private final AdsResource ads;
private final ValidateResource validate;
@@ -83,6 +85,7 @@ public final class FoPost {
private FoPost(ApiClient http) {
this.http = http;
this.posts = new PostsResource(http);
+ this.googleBusiness = new GoogleBusinessResource(http);
this.accounts = new AccountsResource(http);
this.accountGroups = new AccountGroupsResource(http);
this.workspaces = new WorkspacesResource(http);
@@ -164,6 +167,11 @@ public AiResource ai() {
return ai;
}
+ /** Manage a connected Google Business Profile location. */
+ public GoogleBusinessResource googleBusiness() {
+ return googleBusiness;
+ }
+
public InboxResource inbox() {
return inbox;
}
diff --git a/src/main/java/com/fopost/sdk/resource/GoogleBusinessResource.java b/src/main/java/com/fopost/sdk/resource/GoogleBusinessResource.java
new file mode 100644
index 0000000..40f10b8
--- /dev/null
+++ b/src/main/java/com/fopost/sdk/resource/GoogleBusinessResource.java
@@ -0,0 +1,238 @@
+package com.fopost.sdk.resource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fopost.sdk.internal.ApiClient;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Manage a connected Google Business Profile location: the profile itself,
+ * attributes, food menus, services, photos, place action links, verification
+ * and performance.
+ *
+ *
Google grants Business Profile API access per project. Until that grant
+ * lands on a deployment every call here fails with a 503 {@code
+ * configuration_error}.
+ *
+ *
Responses relay Google's own shape, field for field, so they come back as
+ * {@link JsonNode} rather than models we would have to keep chasing.
+ */
+public final class GoogleBusinessResource {
+
+ /** The daily metrics fetched when a caller names none. */
+ public static final List DEFAULT_DAILY_METRICS = List.of(
+ "BUSINESS_IMPRESSIONS_DESKTOP_MAPS",
+ "BUSINESS_IMPRESSIONS_DESKTOP_SEARCH",
+ "BUSINESS_IMPRESSIONS_MOBILE_MAPS",
+ "BUSINESS_IMPRESSIONS_MOBILE_SEARCH",
+ "CALL_CLICKS",
+ "WEBSITE_CLICKS",
+ "BUSINESS_DIRECTION_REQUESTS");
+
+ private final ApiClient http;
+
+ public GoogleBusinessResource(ApiClient http) {
+ this.http = http;
+ }
+
+ /** The connected location, in the Business Information shape. */
+ public JsonNode getLocation(String accountId) {
+ return ApiClient.unwrap(http.get(path(accountId, "/location"), null));
+ }
+
+ /**
+ * Patch the profile. Only the keys the map carries change; a null value
+ * clears that field. Keys are the API's own snake_case names, such as
+ * {@code title}, {@code description}, {@code website_uri} and
+ * {@code primary_phone}.
+ */
+ public JsonNode updateLocation(String accountId, Map fields) {
+ return ApiClient.unwrap(http.request("PATCH", path(accountId, "/location"), body(fields), null));
+ }
+
+ /** The attribute values set on the location. */
+ public JsonNode getAttributes(String accountId) {
+ return getAttributes(accountId, false, null, null, null);
+ }
+
+ /**
+ * The attribute values set on the location, or, with {@code available},
+ * the attributes Google offers for its category and region.
+ */
+ public JsonNode getAttributes(
+ String accountId, boolean available, String categoryName, String regionCode, String languageCode) {
+ Map query = new LinkedHashMap<>();
+ if (available) {
+ query.put("available", "true");
+ }
+ putIfPresent(query, "category_name", categoryName);
+ putIfPresent(query, "region_code", regionCode);
+ putIfPresent(query, "language_code", languageCode);
+ return ApiClient.unwrap(http.get(path(accountId, "/attributes"), query.isEmpty() ? null : query));
+ }
+
+ /** Only the named attributes change; every other one is left alone. */
+ public JsonNode updateAttributes(String accountId, List