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.

MethodPath
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

CodeCauseFix
MISSING_EXTERNAL_IDDestination not published yetWait for publish
ENGAGEMENT_PERMISSIONConnection lacks the permission for the actionReconnect the account
RETWEETS_UNSUPPORTED or COMMENTS_UNSUPPORTEDX access tier does not include this endpointContact support if you believe your tier allows it
X_OUT_OF_CREDITSX developer account has no creditsContact support
INSUFFICIENT_WALLETWallet balance ran outTop up in Settings → Billing
LIKE_FAILED, RETWEET_FAILED or BOOKMARK_FAILEDX rejected the actionCheck the error message for the X reason

See Also