Engagement
Likes, retweets, bookmarks, replies, and quotes on X.
Engagement actions target a live post and bill the wallet like any other X usage. Pass post as described in Analytics. Use the internal id for Onepostly posts. Use the native tweet id plus accountId for outside tweets. destinationId resolves on its own. Pass it alone or with a matching post to pin one destination.
Request Shape
POST calls send JSON with post plus optional accountId and destinationId. DELETE and list calls use the same fields as query params.
In the SDKs the POST body is named engagementTargetBody or quoteBody. DELETE and list calls take post, accountId and destinationId as plain arguments.
Retweets
import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.retweet({
engagementTargetBody: {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8"
},
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"retweeted": true,
"retweetId": "1960956321831854280"
}import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.undoRetweet({
post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"retweeted": false
}import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.listRetweeters({
post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
limit: 25,
});
console.log(response);{
"retweets": {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"subjects": [
{
"scope": "destination",
"subjectId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "x",
"externalPostId": "1960956321831854280",
"status": "ready",
"actors": [
{
"id": "u1",
"username": "jane_doe",
"displayName": "Jane Doe",
"avatarUrl": "https://cdn.onepostly.com/avatars/jane.jpg"
}
],
"nextCursor": null,
"externalUrl": "https://x.com/you/status/1960956321831854280"
}
]
}
}Undoing a retweet needs no retweet id. Pass the original post. Undoing returns the same shape with retweeted: false. The retweeters list returns the accounts that reposted, paginated with nextCursor.
Likes
import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.like({
engagementTargetBody: {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8"
},
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"kind": "like",
"active": true
}import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.unlike({
post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"kind": "like",
"active": false
}Unliking returns the same shape with active: false.
Bookmarks
import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.bookmark({
engagementTargetBody: {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8"
},
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"kind": "bookmark",
"active": true
}import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.removeBookmark({
post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
});
console.log(response);{
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"kind": "bookmark",
"active": false
}Removing a bookmark returns the same shape with active: false.
Replies
Replies are comments on X. Replies to the root post and to other replies are regular tweets in the same conversation.
| Method | Path |
|---|---|
| GET | /v1/comments?post= |
| POST | /v1/comments |
| DELETE | /v1/comments?post=&commentId= |
List returns the conversation replies, root tweet excluded. It covers a recent window, so very old replies may not appear. Paginate with nextCursor. Each comment carries id, text, username, likeCount and timestamp.
Create works like other comments. Pass post plus text, with optional accountId, destinationId and parentCommentId.
import { Configuration, CommentsApi } from "@onepostly/sdk";
const commentsApi = new CommentsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await commentsApi.createComment({
createCommentBody: {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"text": "Love this!",
"parentCommentId": "c0"
},
});
console.log(response);{
"comment": {
"id": "c1",
"text": "Love this!",
"username": "jane_doe",
"likeCount": 12,
"timestamp": "2026-08-25T10:05:00.000Z"
}
}Reply to a reply by passing its id as parentCommentId, or omit it to reply at the top level. Max 280 weighted units, or 25,000 on a paid X subscription. Bills like a regular post create.
Delete removes a reply owned by the connected account. commentId is the reply id on X.
import { Configuration, CommentsApi } from "@onepostly/sdk";
const commentsApi = new CommentsApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await commentsApi.deleteComment({
post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
commentId: "c0",
});
console.log(response);{
"deleted": {
"postId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"destinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"platform": "x",
"commentId": "c0",
"deleted": true
}
}Quote
Quoting an already published post creates a new post quoting the tweet.
import { Configuration, EngagementApi } from "@onepostly/sdk";
const engagementApi = new EngagementApi(
new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);
const response = await engagementApi.quote({
quoteBody: {
"post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"text": "Ship day"
},
});
console.log(response);{
"sourcePostId": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
"sourceDestinationId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"post": {
"id": "b3e1a7c2-8f4d-4e6a-9c1b-2d3e4f5a6b7c",
"text": "Ship day",
"mediaUrls": [
"https://cdn.onepostly.com/media/product.jpg"
],
"mediaKind": "image",
"status": "queued",
"scheduledFor": null,
"timezone": null,
"destinations": [
{
"id": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
"accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
"platform": "x",
"status": "draft",
"externalPostId": "17895695668004550",
"externalUrl": "https://x.com/you/status/17895695668004550",
"errorCode": null,
"errorMessage": null,
"publishedAt": "2026-08-25T10:00:00.000Z",
"metrics": null
}
],
"createdAt": "2026-08-25T10:00:00.000Z",
"updatedAt": "2026-08-25T10:00:00.000Z"
}
}Errors
| Code | Cause | Fix |
|---|---|---|
MISSING_EXTERNAL_ID | Destination not published yet | Wait for publish |
ENGAGEMENT_PERMISSION | Connection lacks the permission for the action | Reconnect the account |
RETWEETS_UNSUPPORTED or COMMENTS_UNSUPPORTED | X access tier does not include this endpoint | Contact support if you believe your tier allows it |
X_OUT_OF_CREDITS | X developer account has no credits | Contact support |
INSUFFICIENT_WALLET | Wallet balance ran out | Top up in Settings → Billing |
LIKE_FAILED, RETWEET_FAILED or BOOKMARK_FAILED | X rejected the action | Check the error message for the X reason |