Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ All notable changes to this project are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
[Semantic Versioning](https://semver.org/).

## [0.4.0] - 2026-10-18

### Added

- `queue()` for the posting queue: `listSlots`, `createSlot`, `updateSlot`, `deleteSlot`, `next` and
`preview`, with the `QueueSlot`, `QueueSlotUpdate`, `QueueSlotDeleted` and `QueueSlotMove` records
and the `QueueSlotParams` builder. `CreatePostParams.queue()` and `UpdatePostParams.queue()` send
`schedule: queue`, which puts the post in the next free slot. Needs `posts`.
- `posts().unpublish` takes a live post down on every network and answers an `UnpublishResult`
with one `Removal` per network. Needs `publish`.
- `connectSessions()`: `create` mints a hosted connect link for one of your own users and `get`
reads the session, with the `ConnectSession` record and the `ConnectSessionParams` builder.
Needs `accounts`.
- `webhooks().deliveries`, `delivery` and `redeliver`: the delivery log of an endpoint, one
attempt, and sending an event again, with the `WebhookDelivery` record. Needs `webhooks`.
- `automations().versions`, `version` and `restoreVersion`: the saved graph history and a restore
that writes a new version on top of it, with `AutomationVersion`, `AutomationVersionPage`,
`AutomationGraph` and `AutomationRestore`. Needs `automations`.
- `logs().list` reads the request log, newest first, with `LogParams` filters and a `LogPage`
carrying the cursor and the window summary. Needs `workspaces`.
- `analytics().inbox` answers the inbox aggregates as an `InboxAnalytics` record. Needs
`analytics`.

### Changed

- The javadoc of every method that builds its path from a helper now names the API operation it
wraps, so the operation list and the SDK can be checked against each other.

## [0.3.0] - 2026-10-18

### Added
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exception per error status.
sources and javadoc.
- Runtime dependencies: `jackson-databind` and `jackson-datatype-jsr310` (2.18.2). HTTP is the
JDK's own `java.net.http.HttpClient` — no HTTP library dependency.
- Version `0.3.0` in `pom.xml`, mirrored by the constant in
- Version `0.4.0` in `pom.xml`, mirrored by the constant in
`src/main/java/com/fopost/sdk/internal/Version.java` (the release workflow enforces the match).

## Downstream Packages
Expand Down Expand Up @@ -162,7 +162,7 @@ anything unwrapped.
mvn -B verify # compile, test, sources jar, javadoc jar — what CI runs
mvn -B test # tests only
mvn -B test -Dtest=RetryTest # one test class
mvn -q install -DskipTests # install 0.3.0 locally, so ../fopost-spring can resolve it
mvn -q install -DskipTests # install 0.4.0 locally, so ../fopost-spring can resolve it
mvn -B -Prelease deploy -DskipTests # GPG-sign and publish to Central (release workflow only)
```

Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ Official Java SDK for the [FoPost](https://fopost.com) API. Schedule and publish
<dependency>
<groupId>com.fopost</groupId>
<artifactId>fopost-java</artifactId>
<version>0.3.0</version>
<version>0.4.0</version>
</dependency>
```

```kotlin
implementation("com.fopost:fopost-java:0.3.0")
implementation("com.fopost:fopost-java:0.4.0")
```

Requires Java 17 or newer. HTTP goes through the JDK's own client; the only dependency is Jackson.
Expand Down Expand Up @@ -97,15 +97,15 @@ long failed = client.posts().stream(PostListParams.create().workspaceId(workspac

| Namespace | Methods |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `posts()` | `list`, `autoPaginate`, `stream`, `get`, `create`, `update`, `delete`, `duplicate`, `publish`, `retry`, `cancel`, `preflight`, `deliveries`, `publishRuns`, `analytics`, `bulkShift`, `bulkLabel`, `bulkDelete`, `validateImport`, `commitImport`, `rollbackImport` |
| `posts()` | `list`, `autoPaginate`, `stream`, `get`, `create`, `update`, `delete`, `duplicate`, `publish`, `retry`, `cancel`, `preflight`, `deliveries`, `publishRuns`, `analytics`, `bulkShift`, `bulkLabel`, `bulkDelete`, `validateImport`, `commitImport`, `rollbackImport`, `unpublish` |
| `workspaces()` | `list`, `get`, `create`, `update`, `delete`, `analytics` |
| `accounts()` | `list`, `get`, `create`, `rename`, `move`, `delete`, `healthSummary`, `health`, `togglePrimary`, `validate`, `refreshToken`, `analytics`, `createTelegramConnectCode`, `getTelegramConnectStatus`, `getTelegramBotCommands`, `setTelegramBotCommands`, `deleteTelegramBotCommands`, `redditSubreddits`, `redditSubredditRules`, `redditFlairs`, `setRedditDefaultSubreddit`, `listSlackChannels`, `listSlackMembers`, `getSlackIdentity`, `updateSlackIdentity`, `getIceBreakers`, `setIceBreakers`, `deleteIceBreakers`, `getPersistentMenu`, `setPersistentMenu`, `deletePersistentMenu`, `getGreeting`, `setGreeting`, `deleteGreeting`, `getWebhookSubscription`, `resubscribeWebhook`, `communities()`, `listDiscordChannels`, `switchDiscordChannel`, `getDiscordIdentity`, `updateDiscordIdentity`, `listDiscordPins`, `deleteDiscordMessage`, `pinDiscordMessage`, `unpinDiscordMessage`, `crosspostDiscordMessage`, `createDiscordThread`, `sendDiscordDm`, `listDiscordEvents`, `getDiscordEvent`, `createDiscordEvent`, `updateDiscordEvent`, `deleteDiscordEvent`, `listDiscordMembers`, `getDiscordMember`, `listDiscordRoles`, `createDiscordRole`, `updateDiscordRole`, `deleteDiscordRole`, `addDiscordMemberRole`, `removeDiscordMemberRole`, `platformMetrics` |
| `accountGroups()` | `list`, `get`, `create`, `update`, `delete`, `setMembers` |
| `labels()` | `list`, `get`, `create`, `update`, `delete` |
| `webhooks()` | `list`, `create`, `update`, `delete`, `test`, `verify` |
| `webhooks()` | `list`, `create`, `update`, `delete`, `test`, `deliveries`, `delivery`, `redeliver`, `verify` |
| `blog()` | `getSettings`, `updateSettings`, `rotateWebsiteSecret`, `testWebsite`, `listKeywords`, `suggestSeeds`, `planKeywords`, `addKeyword`, `updateKeyword`, `deleteKeyword`, `listArticles`, `requestArticle`, `getArticle`, `updateArticle`, `regenerateArticle`, `submitArticle`, `publishArticle`, `rejectArticle`, `deleteArticle` |
| `analytics()` | `overview`, `timeSeries`, `topPosts`, `labels`, `postsTable`, `postingStreak`, `demographics`, `collect`, `decay`, `frequency`, `timeline`, `changes`, `collectPost`, `nativePosts` |
| `automations()` | `list`, `get`, `create`, `update`, `delete`, `toggle`, `runs`, `run`, `trigger`, `stats` |
| `analytics()` | `overview`, `timeSeries`, `topPosts`, `labels`, `postsTable`, `postingStreak`, `demographics`, `collect`, `decay`, `frequency`, `timeline`, `changes`, `collectPost`, `nativePosts`, `inbox` |
| `automations()` | `list`, `get`, `create`, `update`, `delete`, `toggle`, `runs`, `run`, `trigger`, `stats`, `versions`, `version`, `restoreVersion` |
| `media()` | `list`, `limits`, `upload`, `presign`, `complete`, `uploadDirect`, `delete` |
| `ai()` | `credits`, `generateCaption`, `rewrite`, `repurposeUrl` |
| `inbox()` | `list`, `threads`, `conversations`, `unreadCount`, `accounts`, `platforms`, `markThreadRead`, `markConversationRead`, `refresh`, `update`, `editComment`, `reply`, `hide`, `unhide`, `delete`, `like`, `unlike`, `vote`, `pin`, `unpin`, `react`, `startConversation`, `setTyping`, `passThreadControl`, `takeThreadControl`, `handover`, `listApprovals`, `approveReply`, `rejectReply` |
Expand All @@ -122,6 +122,9 @@ long failed = client.posts().stream(PostListParams.create().workspaceId(workspac
| `googleBusiness()` | `getLocation`, `updateLocation`, `getAttributes`, `updateAttributes`, `getMenus`, `replaceMenus`, `getServices`, `replaceServices`, `listMedia`, `addMedia`, `deleteMedia`, `listPlaceActions`, `createPlaceAction`, `updatePlaceAction`, `deletePlaceAction`, `getVerificationOptions`, `startVerification`, `completeVerification`, `getPerformance`, `getSearchKeywords`, `assign` |
| `blogs()` | `listBlogs`, `listArticles`, `getArticle`, `createArticle`, `updateArticle`, `deleteArticle`, `listProducts`, `updateProduct` |
| `validate()` | `post`, `length`, `media`, `subreddit` |
| `queue()` | `listSlots`, `createSlot`, `updateSlot`, `deleteSlot`, `next`, `preview` |
| `connectSessions()` | `create`, `get` |
| `logs()` | `list` |
| `activity()` | `list` |

`accounts().communities()` covers the X communities an account can post into: `list`, `sync`,
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.fopost</groupId>
<artifactId>fopost-java</artifactId>
<version>0.3.0</version>
<version>0.4.0</version>
<packaging>jar</packaging>

<name>fopost-java</name>
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/fopost/sdk/FoPost.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@
import com.fopost.sdk.resource.AutomationsResource;
import com.fopost.sdk.resource.BlogResource;
import com.fopost.sdk.resource.BroadcastsResource;
import com.fopost.sdk.resource.ConnectSessionsResource;
import com.fopost.sdk.resource.ContactsResource;
import com.fopost.sdk.resource.BlogsResource;
import com.fopost.sdk.resource.InboxResource;
import com.fopost.sdk.resource.KnowledgeResource;
import com.fopost.sdk.resource.GoogleAdsResource;
import com.fopost.sdk.resource.GoogleBusinessResource;
import com.fopost.sdk.resource.LabelsResource;
import com.fopost.sdk.resource.LogsResource;
import com.fopost.sdk.resource.MediaResource;
import com.fopost.sdk.resource.PostsResource;
import com.fopost.sdk.resource.QueueResource;
import com.fopost.sdk.resource.SequencesResource;
import com.fopost.sdk.resource.ValidateResource;
import com.fopost.sdk.resource.WebhooksResource;
Expand Down Expand Up @@ -67,8 +70,10 @@ public final class FoPost {

private final ApiClient http;
private final PostsResource posts;
private final QueueResource queue;
private final AccountsResource accounts;
private final AccountGroupsResource accountGroups;
private final ConnectSessionsResource connectSessions;
private final WorkspacesResource workspaces;
private final KnowledgeResource knowledge;
private final LabelsResource labels;
Expand All @@ -81,6 +86,7 @@ public final class FoPost {
private final BroadcastsResource broadcasts;
private final SequencesResource sequences;
private final ActivityResource activity;
private final LogsResource logs;
private final GoogleBusinessResource googleBusiness;
private final WhatsappResource whatsapp;
private final InboxResource inbox;
Expand All @@ -93,13 +99,16 @@ public final class FoPost {
private FoPost(ApiClient http) {
this.http = http;
this.posts = new PostsResource(http);
this.queue = new QueueResource(http);
this.googleBusiness = new GoogleBusinessResource(http);
this.accounts = new AccountsResource(http);
this.accountGroups = new AccountGroupsResource(http);
this.connectSessions = new ConnectSessionsResource(http);
this.workspaces = new WorkspacesResource(http);
this.knowledge = new KnowledgeResource(http);
this.labels = new LabelsResource(http);
this.activity = new ActivityResource(http);
this.logs = new LogsResource(http);
this.webhooks = new WebhooksResource(http);
this.analytics = new AnalyticsResource(http);
this.automations = new AutomationsResource(http);
Expand Down Expand Up @@ -134,6 +143,11 @@ public PostsResource posts() {
return posts;
}

/** The posting queue: weekly slots and the times a queued post would get. */
public QueueResource queue() {
return queue;
}

public AccountsResource accounts() {
return accounts;
}
Expand All @@ -142,6 +156,11 @@ public AccountGroupsResource accountGroups() {
return accountGroups;
}

/** Hosted links that let your own users connect an account without a FoPost login. */
public ConnectSessionsResource connectSessions() {
return connectSessions;
}

public WorkspacesResource workspaces() {
return workspaces;
}
Expand Down Expand Up @@ -180,6 +199,11 @@ public ActivityResource activity() {
return activity;
}

/** The request log: every API call made against a workspace, kept for 30 days. */
public LogsResource logs() {
return logs;
}

public AiResource ai() {
return ai;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/fopost/sdk/internal/Version.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
public final class Version {
private Version() {}

public static final String VALUE = "0.3.0";
public static final String VALUE = "0.4.0";
}
15 changes: 15 additions & 0 deletions src/main/java/com/fopost/sdk/model/AutomationGraph.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.fopost.sdk.model;

import java.util.List;
import java.util.Map;

/** The nodes and edges of a saved automation graph. */
public record AutomationGraph(List<Node> nodes, List<Edge> edges) {

/** {@code config} is echoed back exactly as authored. */
public record Node(String id, String kind, String label, Map<String, Object> config, Integer position, Layout layout) {}

public record Layout(Double x, Double y) {}

public record Edge(String id, String source, String sourceHandle, String target) {}
}
10 changes: 10 additions & 0 deletions src/main/java/com/fopost/sdk/model/AutomationRestore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.fopost.sdk.model;

import java.util.List;
import java.util.Map;

/** The answer to a restore: the new version number, its graph, and the steps compiled from it. */
public record AutomationRestore(Integer version, AutomationGraph graph, List<Step> steps) {

public record Step(Integer position, String nodeId, String actionType, Map<String, Object> actionConfig) {}
}
23 changes: 23 additions & 0 deletions src/main/java/com/fopost/sdk/model/AutomationVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.fopost.sdk.model;

import java.time.Instant;
import java.util.List;

/**
* One saved version of an automation's graph. The list carries {@code nodeCount},
* {@code nodesAdded} and {@code nodesRemoved}; the single-version call carries {@code graph}.
*/
public record AutomationVersion(
Integer version,
String note,
Author createdBy,
Instant createdAt,
Integer nodeCount,
List<NodeRef> nodesAdded,
List<NodeRef> nodesRemoved,
AutomationGraph graph) {

public record Author(String name) {}

public record NodeRef(String id, String kind, String label) {}
}
28 changes: 28 additions & 0 deletions src/main/java/com/fopost/sdk/model/AutomationVersionPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.fopost.sdk.model;

import java.util.Iterator;
import java.util.List;

/** One page of an automation's version history, newest first. */
public record AutomationVersionPage(List<AutomationVersion> data, Meta meta) implements Iterable<AutomationVersion> {

public AutomationVersionPage {
data = data == null ? List.of() : List.copyOf(data);
}

/** {@code totalPages} is at least 1, so an empty history is one empty page. */
public record Meta(Integer page, Integer perPage, Integer total, Integer totalPages) {}

@Override
public Iterator<AutomationVersion> iterator() {
return data.iterator();
}

public int size() {
return data.size();
}

public boolean isEmpty() {
return data.isEmpty();
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/fopost/sdk/model/ConnectSession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.fopost.sdk.model;

import java.time.Instant;
import java.util.List;

/**
* A hosted connect link. {@code url} is present on creation only; {@code accountId} is set once
* the session is used. {@code status} is {@code pending}, {@code used} or {@code expired}.
*/
public record ConnectSession(
String id,
String status,
String externalUserId,
List<String> platforms,
String redirectUrl,
Branding branding,
String accountId,
String url,
Instant expiresAt,
Instant usedAt,
Instant createdAt) {

/** What the hosted page shows as yours. */
public record Branding(String name, String logoUrl) {}
}
54 changes: 54 additions & 0 deletions src/main/java/com/fopost/sdk/model/InboxAnalytics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.fopost.sdk.model;

import java.time.LocalDate;
import java.util.List;

/**
* How the social inbox performed over the period. Aggregates only. {@code deltas} are percentage
* changes against the previous period of the same length; {@code heatmap} is bucketed in
* {@code timeZone}.
*/
public record InboxAnalytics(
Totals totals,
Deltas deltas,
List<Point> series,
List<PlatformRow> byPlatform,
List<TypeRow> byType,
List<AccountRow> byAccount,
List<ResponseTimeRow> responseTime,
List<HeatmapCell> heatmap,
String timeZone) {

/** {@code medianResponseMinutes} is null when nothing was replied to. */
public record Totals(
Integer received,
Integer sent,
Integer answered,
Integer open,
Integer conversations,
Double medianResponseMinutes) {}

public record Deltas(Double received, Double sent, Double answered) {}

public record Point(LocalDate date, Integer received, Integer sent, Integer answered) {}

public record PlatformRow(String platform, Integer received, Integer sent) {}

/** {@code type} is comment, mention, reply, dm or review. */
public record TypeRow(String type, Integer count) {}

public record AccountRow(
String accountId,
String platform,
String username,
String name,
String avatar,
Integer received,
Integer sent) {}

/** {@code bucket} is under_1h, 1h_6h, 6h_24h, 1d_3d or over_3d. */
public record ResponseTimeRow(String bucket, Integer count) {}

/** {@code weekday} 0 is Sunday; {@code hour} is 0 to 23. */
public record HeatmapCell(Integer weekday, Integer hour, Integer count) {}
}
20 changes: 20 additions & 0 deletions src/main/java/com/fopost/sdk/model/LogEntry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.fopost.sdk.model;

import java.time.Instant;

/**
* One API request made against a workspace. {@code route} is the pattern, such as
* {@code /v1/posts/{id}}; {@code message} is the error text of a failed request.
*/
public record LogEntry(
Long id,
String workspaceId,
Instant time,
String authMethod,
String method,
String route,
String path,
Integer statusCode,
Long durationMs,
String platform,
String message) {}
Loading
Loading