Publishing

Threads, Replies and Quotes

Publish a thread of replies, reply to an existing post, or quote one on X, Threads and Bluesky.

Publish a post with a chain of replies under it, publish a post as a reply to another post, or quote a post. These work on X, Threads and Bluesky.

PlatformthreadreplyToPostIdquotePostId
X (Twitter)No. Use Comments
Threads
Bluesky
FieldWhereEffect
threadRequest bodyPublishes each item as a reply under the first post, in order
replyToPostIdPlatform options, threads or blueskyPublishes the post as a reply to an existing post
quotePostIdPlatform options, x, threads or blueskyQuotes an existing post

Publish a Thread

Add thread to a Create Post request. The top-level text, mediaKind and media make up the first post, and each thread item is one reply under it.

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

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

const response = await postsApi.createPost({
  createPostBody: {
    "text": "Three things we shipped this month.",
    "thread": [
      {
        "text": "1. Scheduling in any IANA time zone."
      },
      {
        "text": "2. Remote delete for whole threads."
      },
      {
        "text": "3. Webhooks for posts made outside Onepostly."
      }
    ],
    "accounts": [
      {
        "accountId": "acc_YOUR_X_ACCOUNT_ID"
      },
      {
        "accountId": "acc_YOUR_THREADS_ACCOUNT_ID"
      }
    ]
  },
});
console.log(response);
{
  "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
  "data": [
    {
      "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
      "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
      "accountId": "acc_YOUR_X_ACCOUNT_ID",
      "platform": "x",
      "source": "onepostly",
      "status": "publishing",
      "text": "Three things we shipped this month.",
      "mediaKind": "text",
      "media": [],
      "thread": [
        {
          "text": "1. Scheduling in any IANA time zone.",
          "media": []
        },
        {
          "text": "2. Remote delete for whole threads.",
          "media": []
        },
        {
          "text": "3. Webhooks for posts made outside Onepostly.",
          "media": []
        }
      ],
      "scheduledFor": null,
      "timezone": null,
      "options": {},
      "platformPostId": null,
      "url": null,
      "error": null,
      "publishedAt": null,
      "metrics": null,
      "createdAt": "2027-03-15T09:00:00.000Z",
      "updatedAt": "2027-03-15T09:00:00.000Z"
    },
    {
      "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a02",
      "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
      "accountId": "acc_YOUR_THREADS_ACCOUNT_ID",
      "platform": "threads",
      "source": "onepostly",
      "status": "publishing",
      "text": "Three things we shipped this month.",
      "mediaKind": "text",
      "media": [],
      "thread": [
        {
          "text": "1. Scheduling in any IANA time zone.",
          "media": []
        },
        {
          "text": "2. Remote delete for whole threads.",
          "media": []
        },
        {
          "text": "3. Webhooks for posts made outside Onepostly.",
          "media": []
        }
      ],
      "scheduledFor": null,
      "timezone": null,
      "options": {},
      "platformPostId": null,
      "url": null,
      "error": null,
      "publishedAt": null,
      "metrics": null,
      "createdAt": "2027-03-15T09:00:00.000Z",
      "updatedAt": "2027-03-15T09:00:00.000Z"
    }
  ]
}
RuleDetail
Length1 to 25 items
Each itemtext and up to 20 media files, given as id or url like the post's own media. An item needs text or media
First postNeeds text or media
Text limitsEach item's text has to fit every account of the request
Per accountThread items cannot be set per account. Every account publishes the same replies
PlatformsEvery account of the request has to be on X, Threads or Bluesky. Otherwise the request returns 400 VALIDATION_ERROR

Each platform limits the media of one reply, such as 4 images on X. The limits are in each platform's Media Requirements section.

The post returns its replies in thread, and has one id for the whole thread.

  • On X, each post of the thread is charged from the wallet. See X Wallet.
  • Deleting the post deletes every post of the thread. See Delete a Post.
  • If a reply fails after earlier posts went live, the post ends as failed. See Threads That Fail Partway.
  • To change the thread of a scheduled post, send the whole new thread with Edit a Scheduled Post.

Reply to a Post

Set replyToPostId in the account's threads or bluesky options. The post publishes as a reply to that post.

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

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

const response = await postsApi.createPost({
  createPostBody: {
    "text": "Here is how we set this up.",
    "accounts": [
      {
        "accountId": "acc_YOUR_THREADS_ACCOUNT_ID",
        "threads": {
          "replyToPostId": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73"
        }
      }
    ]
  },
});
console.log(response);
{
  "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e02",
  "data": [
    {
      "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a02",
      "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e02",
      "accountId": "acc_YOUR_THREADS_ACCOUNT_ID",
      "platform": "threads",
      "source": "onepostly",
      "status": "publishing",
      "text": "Here is how we set this up.",
      "mediaKind": "text",
      "media": [],
      "thread": [],
      "scheduledFor": null,
      "timezone": null,
      "options": {
        "threads": {
          "replyToPostId": "18047285913046723"
        }
      },
      "platformPostId": null,
      "url": null,
      "error": null,
      "publishedAt": null,
      "metrics": null,
      "createdAt": "2027-03-15T09:00:00.000Z",
      "updatedAt": "2027-03-15T09:00:00.000Z"
    }
  ]
}

On X, a reply to a post is a comment. Use Comment on a Post instead.

Quote a Post

Set quotePostId in the account's x, threads or bluesky options. The post publishes with the quoted post embedded.

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

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

const response = await postsApi.createPost({
  createPostBody: {
    "text": "Worth reading if you run scheduled jobs.",
    "accounts": [
      {
        "accountId": "acc_YOUR_X_ACCOUNT_ID",
        "x": {
          "quotePostId": "1960956321831854280"
        }
      }
    ]
  },
});
console.log(response);
{
  "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
  "data": [
    {
      "id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
      "groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
      "accountId": "acc_YOUR_X_ACCOUNT_ID",
      "platform": "x",
      "source": "onepostly",
      "status": "publishing",
      "text": "Worth reading if you run scheduled jobs.",
      "mediaKind": "text",
      "media": [],
      "thread": [],
      "scheduledFor": null,
      "timezone": null,
      "options": {
        "x": {
          "quotePostId": "1960956321831854280"
        }
      },
      "platformPostId": null,
      "url": null,
      "error": null,
      "publishedAt": null,
      "metrics": null,
      "createdAt": "2027-03-15T09:00:00.000Z",
      "updatedAt": "2027-03-15T09:00:00.000Z"
    }
  ]
}

Post References

replyToPostId and quotePostId name the post in one of two ways.

ValueExampleRule
A pst_ idpst_3c8a2d6e91b44f7a8e250d6b4c1f9a73A post of this workspace that is live on the same platform. Posts made outside Onepostly work too, once a sync found them
The platform's own id1960956321831854280The numeric post id on X and Threads, or the at:// URI on Bluesky. Use it for posts of other accounts. The platform decides whether the account can reply to or quote the post
  • A pst_ id of a post on another platform returns 400 VALIDATION_ERROR.
  • A pst_ id of a post that is not live, such as a scheduled or failed post, returns 409 POST_NOT_PUBLISHED. Wait for post.published, or use the platform's own id.
  • A platform id is sent to the platform as it is. If the platform cannot find the post, the post fails when it publishes, with the code in error.

Platform Differences

Media per reply, text limits and the codes each platform returns are on its page.

  • X. Replies to existing posts go through Comments.
  • Threads.
  • Bluesky. A Bluesky reference has to be an at:// URI or a pst_ id.

Errors

CodeHTTPCauseFix
VALIDATION_ERROR400A thread item is empty, an account's platform has no threads, or a pst_ reference is on another platformFix the field named in param or error
INSUFFICIENT_WALLET402The wallet does not cover every post of an X threadTop up the wallet. See X Wallet
POST_NOT_PUBLISHED409quotePostId or replyToPostId names a pst_ post that is not liveWait for post.published, or use the platform's own post id
INVALID_QUOTE, INVALID_REPLYPost errorThe Bluesky post to quote or reply to is not a valid at:// URI, or could not be foundUse the pst_ id or at:// URI of an existing post

Every other code is in Errors.