Publishing
Scheduling and Editing
Schedule a post for a local time in any time zone, change it while it waits, or cancel it before it publishes.
Set a local date and time and a time zone, and the post publishes then. While it waits, you can change it or cancel it.
Schedule a Post
Add scheduledFor and timezone to a Create Post request.
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": "Join our product webinar next week.",
"scheduledFor": "2027-03-15T09:00:00",
"timezone": "America/New_York",
"accounts": [
{
"accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID"
}
]
},
});
console.log(response);{
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e02",
"data": [
{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a02",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e02",
"accountId": "acc_YOUR_LINKEDIN_ACCOUNT_ID",
"platform": "linkedin",
"source": "onepostly",
"status": "scheduled",
"text": "Join our product webinar next week.",
"mediaKind": "text",
"media": [],
"thread": [],
"scheduledFor": "2027-03-15T09:00:00",
"timezone": "America/New_York",
"options": {},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-08T15:00:00.000Z",
"updatedAt": "2027-03-08T15:00:00.000Z"
}
]
}Each post is created with the status scheduled, and the post.scheduled webhook fires. The post returns the schedule as you sent it, with seconds added.
| Rule | Detail |
|---|---|
scheduledFor format | A local date and time, YYYY-MM-DDTHH:MM, with optional seconds and milliseconds. No Z and no offset such as -05:00. Those return 400 VALIDATION_ERROR |
timezone | An IANA time zone, such as America/New_York or Europe/Berlin. Without it, the time is in UTC. timezone without scheduledFor returns 400 VALIDATION_ERROR |
| Daylight saving | The time is read with the zone's rules on that date. 09:00 in America/New_York is 9 AM local time in winter and in summer |
| Furthest ahead | 1 year |
| Less than 1 minute ahead | Publishes right away. The post starts as publishing |
| In the past | Up to 5 minutes in the past publishes right away. Further in the past returns 400 VALIDATION_ERROR |
| Invalid dates | A date that does not exist, such as 2027-02-30T09:00:00, returns 400 VALIDATION_ERROR |
When It Publishes
The request is checked against every cross-posting rule when you schedule it, except the publishing limits. A few things are checked again when the post is due.
| At the scheduled time | Outcome |
|---|---|
| The account is over its publishing limit | The post fails with RATE_LIMITED |
| The account was disconnected, or its access was revoked | The post fails with CONNECTION_INACTIVE |
| The wallet no longer covers an X post. X posts are charged when they publish, not when you schedule them | The post fails with INSUFFICIENT_WALLET |
| The workspace is blocked on billing | The post stays scheduled and publishes once billing is fixed. See Billing Blocks |
Uploaded files that a scheduled post uses are kept until it publishes, even past their usual 7-day expiry. See Expiry.
Edit a Scheduled Post
PATCH /v1/posts/{id} changes a post while it is scheduled. Only the fields you send change, and the result is checked again like a new post.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.updatePost({
id: "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
updatePostBody: {
"scheduledFor": "2027-03-16T09:00:00",
"options": {
"instagram": {
"firstComment": "Register at the link in our profile."
}
}
},
});
console.log(response);{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_INSTAGRAM_ACCOUNT_ID",
"platform": "instagram",
"source": "onepostly",
"status": "scheduled",
"text": "Join our product webinar next week.",
"mediaKind": "text",
"media": [],
"thread": [],
"scheduledFor": "2027-03-16T09:00:00",
"timezone": "America/New_York",
"options": {
"instagram": {
"firstComment": "Register at the link in our profile."
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T10:00:00.000Z"
}| Field | Effect |
|---|---|
text | Replaces the post's text |
media | Replaces the files. Without mediaKind, the kind is worked out again from the new files |
mediaKind | Changes the kind. Leave it out to keep the current one |
thread | Replaces the whole thread. An empty list removes it |
scheduledFor | Moves the post. It has to be at least 1 minute ahead. Leave it out to keep the current time |
timezone | Changes the time zone. Without it, a new scheduledFor keeps the post's time zone |
options | Changes options under the post's platform id, as in Create Post. Only the options you send change, and null removes one |
- An edit applies to this post only. If the post shares its group with posts on other accounts, it gets a new
groupIdand the other posts keep theirs. optionsfor another platform returns400 VALIDATION_ERRORwith the key inparam, such asoptions.tiktok.- An edit to the
textorthreadof an X post returns402 INSUFFICIENT_WALLETwhen the wallet does not cover the edited post. - You cannot change the account. Cancel the post and create a new one.
- A post that is no longer
scheduled, or that was made outside Onepostly, returns409 NOT_EDITABLE. - To publish a scheduled post now, cancel it and create it again without
scheduledFor.
The response is the updated post, and the post.updated webhook fires.
Cancel a Scheduled Post
DELETE /v1/posts/{id} cancels a post that has not been published yet. The post keeps its record with the status cancelled, and the post.cancelled webhook fires.
import { Configuration, PostsApi } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
const response = await postsApi.deletePost({
id: "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
});
console.log(response);{
"id": "pst_3c8a2d6e91b44f7a8e250d6b4c1f9a73",
"groupId": "grp_7d2e5b1a9c4f4a68b3e07f1c2d9a6e45",
"accountId": "acc_YOUR_INSTAGRAM_ACCOUNT_ID",
"platform": "instagram",
"source": "onepostly",
"status": "cancelled",
"text": "Join our product webinar next week.",
"mediaKind": "text",
"media": [],
"thread": [],
"scheduledFor": "2027-03-16T09:00:00",
"timezone": "America/New_York",
"options": {
"instagram": {
"firstComment": "Register at the link in our profile."
}
},
"platformPostId": null,
"url": null,
"error": null,
"publishedAt": null,
"metrics": null,
"createdAt": "2027-03-15T09:00:00.000Z",
"updatedAt": "2027-03-15T11:00:00.000Z"
}| Status | What happens |
|---|---|
scheduled | The post becomes cancelled |
publishing | The post becomes cancelled if it has not reached the platform yet. Otherwise you get 409 POST_PUBLISHING. Try again once its status is final |
cancelled | Nothing changes |
Cancelling one post does not affect the other posts of its group. A cancelled post cannot be scheduled again, so create a new post instead. To remove a post that is already live, see Delete a Post.
Errors
| Code | HTTP | Cause | Fix |
|---|---|---|---|
VALIDATION_ERROR | 400 | The schedule is invalid, an edit leaves scheduledFor less than 1 minute ahead, or the edited post breaks a cross-posting rule | Fix the field named in param |
INSUFFICIENT_WALLET | 402 | The wallet does not cover the X posts you schedule, or an edit to the text or thread of an X post. Nothing is created or changed | Top up the wallet. See X Wallet |
ACCOUNT_NOT_FOUND | 404 | The post's account was disconnected | Create a new post for a connected account |
CONNECTION_INACTIVE | 409 | The post's account needs a reconnect | Reconnect the account, then edit again |
NOT_EDITABLE | 409 | The post is not scheduled, or was made outside Onepostly | Only edit scheduled posts |
POST_PUBLISHING | 409 | The post is being published right now, so it cannot be cancelled | Try again once its status is final |
Every other code is in Errors.