Engagement

Repost Threads posts and manage replies through Onepostly.

Threads engagement is reposts. Onepostly maps the X style retweet endpoints onto Threads reposts, so the same integration works on both platforms. Pass post as described in Analytics. Use the internal id for Onepostly posts. Use the native post id plus accountId for outside posts.

Reposts

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": "threads",
  "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": "threads",
  "retweeted": false
}

Undoing removes exactly the repost this connection created, so no id is needed. Pass the original post. It returns the same shape with retweeted: false.

Successful calls emit the engagement.retweeted and engagement.unretweeted webhooks.

Quotes

Quote a published Threads post by passing quoteTweetId on create. See Posts → Quote. Quoting emits engagement.quoted plus the usual publish events.

Likes and bookmarks do not exist on Threads.

Comments

Manage replies on a published Threads post. Replying works on your own connected account posts.

List

Returns the post replies. Top level replies include their nested replies.

import { Configuration, CommentsApi } from "@onepostly/sdk";

const commentsApi = new CommentsApi(
  new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);

const response = await commentsApi.listComments({
  post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
  limit: 25,
});
console.log(response);
{
  "comments": {
    "post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
    "subjects": [
      {
        "scope": "destination",
        "subjectId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
        "accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
        "platform": "threads",
        "externalPostId": "17895695668004550",
        "status": "ready",
        "comments": [
          {
            "id": "c1",
            "text": "Love this!",
            "username": "jane_doe",
            "likeCount": 12,
            "timestamp": "2026-08-25T10:05:00.000Z",
            "parentId": null
          }
        ],
        "nextCursor": null,
        "error": null,
        "externalUrl": "https://www.threads.com/@you/post/17895695668004550"
      }
    ]
  }
}
QueryRequiredNotes
postYesInternal post id or native post id. Native ids require accountId
accountIdFor native idsDisambiguates the target account. Also narrows internal posts with several destinations
destinationIdNoGlobally unique. Resolves on its own or narrows one destination inside an internal post
limitNo1-50, default 25
cursorNoPagination cursor from the previous response nextCursor. Requires a single resolved target

Create

Creates a top level reply, or a nested reply when parentCommentId is set.

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"
  }
}
BodyRequiredNotes
postYesInternal post id or native post id. Native ids require accountId
textYesMax 500 characters
accountIdFor native idsTarget account for native ids. Also disambiguates internal posts
destinationIdNoGlobally unique. Resolves on its own or targets one destination inside an internal post
parentCommentIdNoNest the reply under another reply. Omit to reply to the post itself

Delete

Deletes a reply owned by the connected account. commentId is the reply id on Threads. Get it from List above (comments[].id). The same id works for hide below.

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": "threads",
    "commentId": "c0",
    "deleted": true
  }
}

Hide or unhide

Hides a reply so only its author still sees it, or reveals it again with hidden: false.

import { Configuration, CommentsApi } from "@onepostly/sdk";

const commentsApi = new CommentsApi(
  new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);

const response = await commentsApi.hideComment({
  body: {
    "post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
    "commentId": "c0",
    "hidden": true
  },
});
console.log(response);
{
  "hidden": {
    "postId": "string",
    "destinationId": "string",
    "platform": "threads",
    "commentId": "string",
    "hidden": false
  }
}

If a call fails with a permission error, reconnect the Threads connection once.

Outside Posts

Posts published outside Onepostly work the same way once they are synced. Sync by URL then use the native id plus accountId in every comments call. Omit url and postId to refresh the recent posts of the account in bulk.

import { Configuration, PostsApi } from "@onepostly/sdk";

const postsApi = new PostsApi(
  new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);

const response = await postsApi.syncExternal({
  syncExternalBody: {
    "accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
    "url": "https://www.threads.com/@you/post/DbNQAb8ABC1"
  },
});
console.log(response);
{
  "synced": {
    "postsFound": 1,
    "postsSynced": 1,
    "skipped": false
  },
  "found": true,
  "post": {
    "id": "e8f9a0b1-c2d3-4e5f-8a6b-c7d8e9f0a1b2",
    "accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
    "platform": "threads",
    "externalPostId": "DbNQAb8ABC1",
    "externalUrl": "https://www.threads.com/@you/post/DbNQAb8ABC1",
    "mediaKind": "text",
    "publishedAt": "2026-08-25T10:00:00.000Z",
    "syncedAt": "2026-08-25T10:00:00.000Z",
    "analyticsStatus": "ready"
  }
}
import { Configuration, CommentsApi } from "@onepostly/sdk";

const commentsApi = new CommentsApi(
  new Configuration({ apiKey: process.env.ONEPOSTLY_API_KEY })
);

const response = await commentsApi.listComments({
  post: "1699f415-7fb6-43f4-9d2a-c447491f32a8",
  limit: 25,
});
console.log(response);
{
  "comments": {
    "post": "1699f415-7fb6-43f4-9d2a-c447491f32a8",
    "subjects": [
      {
        "scope": "destination",
        "subjectId": "d4e5f6a7-8b9c-4d0e-8f1a-2b3c4d5e6f7a",
        "accountId": "a1b2c3d4-e5f6-4a7b-8c9d-e0f1a2b3c4d5",
        "platform": "threads",
        "externalPostId": "17895695668004550",
        "status": "ready",
        "comments": [
          {
            "id": "c1",
            "text": "Love this!",
            "username": "jane_doe",
            "likeCount": 12,
            "timestamp": "2026-08-25T10:05:00.000Z",
            "parentId": null
          }
        ],
        "nextCursor": null,
        "error": null,
        "externalUrl": "https://www.threads.com/@you/post/17895695668004550"
      }
    ]
  }
}

Errors

CodeCauseFix
MISSING_EXTERNAL_IDDestination not published yetWait for publish
ENGAGEMENT_PERMISSIONConnection lacks the repost permissionReconnect the account
RETWEET_FAILED or UNRETWEET_FAILEDThreads rejected the actionCheck the error message for the Threads reason
COMMENTS_PERMISSIONConnection lacks threads_manage_repliesReconnect the account
TEXT_REQUIREDEmpty reply textAdd message text
TEXT_TOO_LONGOver 500 charactersTrim the text
COMMENT_CREATE_FAILEDThreads rejected the replyCheck the error message for the Threads reason
COMMENT_HIDE_FAILEDThreads rejected the hide toggleCheck the error message for the Threads reason

See Also

  • Posts
  • Analytics
  • Webhooks for comment.created, comment.deleted, engagement.retweeted and engagement.unretweeted events