Scheduling
Schedule posts and threads for future publishing on X, Bluesky, LinkedIn, Threads, Mastodon, Instagram, Facebook, and Telegram, and save drafts to finish later.
How Scheduling Works
Add schedule_at to any create request and Chirpie publishes it for you. The post is
stored with status scheduled, and the scheduler sends it to the platform at that time.
It works identically on every platform you can connect.
curl -X POST https://chirpie.ai/api/v1/posts \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "YOUR_ACCOUNT_ID",
"text": "This posts at noon UTC tomorrow",
"schedule_at": "2026-03-24T12:00:00Z"
}'Timing
- The request field is
schedule_at. Sendingscheduled_at(the field name in the response) is rejected with400 Unknown field 'scheduled_at'. Did you mean 'schedule_at'?rather than being ignored schedule_atmust be a future ISO 8601 datetime. The one exception is a draft, where it is only the time you have in mind: it may be any time at all, and a draft whose remembered time passes simply stays a draft- Any ISO 8601 timestamp with a timezone is accepted:
2026-03-24T12:00:00Z,2026-03-24T14:00:00+02:00and2026-03-24T07:00:00-05:00all work, and all three name the same instant. Chirpie converts to UTC on the way in, so there is no need to do the arithmetic yourself. - A timestamp with no timezone (
2026-03-24T12:00:00) is read as a local wall-clock time in thetimezoneon the request, or the one saved on your account. With neither, it is rejected as ambiguous. See Timezones. - Relative offsets (
+30m,2 hours) are not accepted. Send an absolute timestamp. - The scheduler runs every 5 minutes, so posts publish within ~5 minutes of their scheduled time
- Everything Chirpie returns (
scheduled_at,published_at) is in UTC
Rejection messages
A bad schedule_at returns 400 bad_request with a message that names the
problem:
| What you sent | Message |
|---|---|
2026-03-24T12:00:00, with no timezone and none saved on the account | schedule_at is missing a timezone, so the instant it names is ambiguous. Add 'Z' for UTC or an offset like '+02:00', send a timezone field such as 'America/New_York', or save a timezone on your account. Example: 2027-01-01T12:00:00Z |
timezone: "+02:00" | timezone must be an IANA timezone name such as 'America/New_York' or 'Europe/Berlin', not '+02:00'. |
2026-02-30T09:30 (a date that does not exist) | schedule_at is not a real date and time: '2026-02-30T09:30'. Check the day of the month and the hour. |
+30m (relative offset) | schedule_at must be an absolute ISO 8601 timestamp, not a relative offset like '+30m'. Example: 2027-01-01T12:00:00Z |
| Anything else malformed | schedule_at must be an ISO 8601 timestamp. Example: 2027-01-01T12:00:00Z |
Timezones
You can schedule in local time. schedule_at has always taken an absolute instant, and
still does. What is new is that it also takes a plain local time, with the zone named
separately, which is how a human actually says when they want something published.
Three ways to name a time
| What you send | How it is read |
|---|---|
"schedule_at": "2026-11-01T13:30:00Z", or any offset such as "2026-11-01T09:30:00-04:00" | An absolute instant. The instant is read straight off the offset, so timezone plays no part in working it out, and nothing about this changed. A timezone sent anyway still has to be a real IANA name: a misspelled one is refused rather than ignored, because silently dropping it is how a caller believes their times are being read somewhere they are not |
"schedule_at": "2026-11-01T09:30:00" plus "timezone": "America/New_York" | 09:30 wall-clock in New York on that date, converted using the offset actually in force then |
"schedule_at": "2026-11-01T09:30:00" with a timezone saved on your account | The same, with your saved zone standing in |
A local time with no zone on the request and none saved is the one case that is refused, with
a 400. Guessing which hour you meant is how a post goes out at the wrong time.
curl -X POST https://chirpie.ai/api/v1/posts \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "YOUR_ACCOUNT_ID",
"text": "Half nine, my time.",
"schedule_at": "2026-11-01T09:30:00",
"timezone": "America/New_York"
}'timezone is accepted on POST /api/v1/posts, POST /api/v1/threads and
PATCH /api/v1/posts/:id.
Saving a timezone on your account
Set it in the dashboard under Settings. Once saved, a local schedule_at needs no
timezone on the request, and the dashboard shows queued times in it. A timezone on a
request always wins over the saved one, so a job publishing on behalf of somewhere else can
say so per call without changing your account.
Why an IANA name, and not an offset
timezone takes an IANA name such as America/New_York or Europe/Berlin. A fixed offset
like "+02:00" is refused on purpose, and this is the reason:
Worked example. You are queuing 09:30 on 1 November in New York, and you are doing it in October, while the clocks there still say UTC-4.
| Wall time | Zone | Offset in force on that date | Published at |
|---|---|---|---|
| 09:30 on 1 October 2026 | America/New_York | UTC-4 (daylight time) | 2026-10-01T13:30:00Z |
| 09:30 on 1 November 2026 | America/New_York | UTC-5 (standard time) | 2026-11-01T14:30:00Z |
The clocks in New York go back on 1 November 2026, so the two 09:30s are an hour apart in
UTC. A client that computes today's offset and sends 09:30:00-04:00 for the November post
publishes it at 08:30 local, an hour early. The zone name survives the change; the offset
does not. So send the name and let Chirpie work out the offset for the date you named.
The same applies in the other direction in spring, and to every zone that observes daylight
saving. A zone that does not observe it (UTC, Asia/Tokyo) is unaffected either way, and
naming it still costs you nothing.
If you genuinely want a fixed offset, put it on schedule_at itself. That is an absolute
instant, and Chirpie never reinterprets one.
The two hours a year that are not simple
A local time on the night the clocks change can be ambiguous or impossible, and Chirpie resolves both the same way every scheduler does. It never publishes earlier than the time you named.
| The wall time you named | What happens |
|---|---|
| Happens twice (the clocks go back) | The first of the two occurrences. 01:30 on 1 November 2026 in America/New_York is 05:30Z, on the pre-change offset |
| Does not exist (the clocks go forward) | Shifted forward by the length of the gap. 02:30 on 8 March 2026 in America/New_York publishes at 03:30 local, which is 07:30Z |
This is right on zones whose change is not a whole hour, such as Australia/Lord_Howe,
which moves by thirty minutes.
Everything Chirpie returns is UTC
scheduled_at, published_at and every other timestamp in a response are UTC, with a Z.
A timezone on the request tells Chirpie how to read what you sent; it does not change what
comes back. Format for display on your side.
Post Status Flow
draft → scheduled → publishing → published
→ failed (retry up to 3 times)
draft → published (sent straight from the draft)A draft moves only when you promote it. It never publishes on its own, and it never enters the queue until it is scheduled. See Drafts.
Thread Scheduling
When scheduling a thread, all posts share the same schedule_at. The thread publishes atomically: if any part fails, every part already published is deleted from the platform and the whole thread is retried like any other scheduled post. If a published part cannot be deleted, the thread is marked failed instead of retried, so it is never posted twice.
curl -X POST https://chirpie.ai/api/v1/threads \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "YOUR_ACCOUNT_ID",
"posts": [
{ "text": "Thread post 1" },
{ "text": "Thread post 2" }
],
"schedule_at": "2026-03-24T12:00:00Z"
}'A scheduled first comment
A scheduled post keeps its first comment with
it. first_comment.status stays pending until the post publishes, and both
go out at the scheduled time, the post first.
Scheduled publishing options
A scheduled post keeps its
publishing options too, so an Instagram
story or reel, or a Facebook Page story, publishes at the scheduled time
exactly as it was written. Change them while the post is still queued with
configuration on PATCH /api/v1/posts/:id, and send configuration: {} to
put it back to a plain feed post.
Scheduled Post Limits
Scheduled posts count against a separate monthly quota:
| Plan | Scheduled Posts/mo |
|---|---|
| Free | 25 |
| Agent | 150 |
| Starter | 500 |
| Pro | 2,500 |
| Scale | 12,500 |
| Enterprise | Unlimited |
Changing a Scheduled Post
A queued post can be edited in place, so a typo does not mean deleting it and starting
again. PATCH /api/v1/posts/:id takes text,
media, media_ids, media_urls, first_comment, configuration and
schedule_at, all optional, and on a draft draft and publish as well:
# Fix the words, keep the time
curl -X PATCH https://chirpie.ai/api/v1/posts/POST_ID \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Now with the typo fixed"}'
# Move it, keep the words
curl -X PATCH https://chirpie.ai/api/v1/posts/POST_ID \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule_at": "2027-04-02T09:00:00Z"}'Leaving schedule_at out keeps the time the post already has. An edit never
publishes a queued post, so a text change is only ever a text change. A draft is
the one thing an edit can send, and only when the request asks it to. See
Drafts.
A new time has to satisfy the same rules as the original: it must be in the future, and at least 5 minutes from any other scheduled post on the same account (the post being moved does not count against itself). Rescheduling one post of a thread moves every part of it, since a thread publishes as a unit.
Only a post that has not published yet can be edited. One that is publishing,
published, failed or deleted answers 409 post_not_editable. Editing does not count
against your quota.
From the dashboard, open a scheduled post on the Schedule page and use Edit for the whole post or Reschedule for the time alone.
Canceling a Scheduled Post
Delete the post before its scheduled time to cancel it:
curl -X DELETE https://chirpie.ai/api/v1/posts/POST_ID \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY"Cancelling one post in a scheduled thread cancels the thread, because a thread is published as a unit and is cancelled as one. The response lists every id it cancelled in cancelled_ids.
Disconnecting or Deactivating an Account
Disconnecting an account, or deactivating it to free a slot against your plan's account limit, cancels every scheduled post queued against it. A disconnected or deactivated account cannot publish, so leaving those posts queued would mean promising a delivery that can never happen.
Cancelled posts return to your monthly quota (both the post allowance and the scheduled
post allowance), and the number cancelled comes back as canceled_posts on
PATCH /api/v1/accounts/{id} and
on DELETE /api/v1/accounts/{id}. To
see the number in advance, read scheduled_posts on the account in
GET /api/v1/accounts.
Reconnecting or reactivating the account does not restore them, so schedule them afresh.
Minimum Spacing
Scheduled posts for the same account must be at least 5 minutes apart. If you try to schedule a post within 5 minutes of an existing scheduled post for the same account, the API returns 400:
{ "error": { "code": "bad_request", "message": "Scheduled posts must be at least 5 minutes apart for the same account." } }This prevents platform API rate limit issues and ensures reliable delivery.
Retry Behavior
Failed posts are retried up to 3 times with a 5-minute delay between attempts. After 3 failures, the post is marked as failed with an error_message describing the issue. retry_count on the post says how many attempts have been made, so a post still scheduled with a non-zero count has already missed one.
When the connection itself dies
Retrying cannot fix a credential the platform has stopped accepting. If a connected account stops working, Chirpie switches it off, cancels everything scheduled against it and returns the quota, and marks the account with an inactive_reason. Each cancelled post carries error_message: "connection_lost".
See When a connection dies for how to detect it, what the email says, and how to get back to publishing.
Drafts
A draft is a post saved without being sent. Nothing reaches the platform,
nothing is queued, and nothing counts against your monthly post quota or your
scheduled post quota until you promote it. A schedule_at on a draft is only
the time you have in mind: it is remembered, and the post does not publish at
it.
# Save a draft, with a time in mind
curl -X POST https://chirpie.ai/api/v1/posts \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"account_id": "YOUR_ACCOUNT_ID",
"text": "Half an idea. Finish it later.",
"schedule_at": "2026-03-24T12:00:00Z",
"draft": true
}'Promote it when it is ready. schedule_at queues it, publish: true sends it
now, and the two are never valid together:
# Queue the draft for a time
curl -X PATCH https://chirpie.ai/api/v1/posts/DRAFT_ID \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"schedule_at": "2027-03-24T12:00:00Z"}'
# Send it now
curl -X PATCH https://chirpie.ai/api/v1/posts/DRAFT_ID \
-H "Authorization: Bearer chirpie_sk_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"publish": true}'Promotion runs every rule a create runs, the 5 minute spacing and the character
limits included, and spends the quota the draft never spent. If anything refuses
it, the draft is left exactly as it was. To change the time a draft remembers
without queueing it, send draft: true alongside schedule_at.
A draft whose remembered time passes simply stays a draft: nothing publishes
late, and nothing is cancelled. List drafts with
GET /api/v1/posts?status=draft.
From the dashboard, Save draft sits beside Post and Schedule in the compose dialog, and saved drafts are on the Drafts tab of the Schedule page, newest first. Open puts one back in the dialog, where you can save it again, schedule it, or post it.
Full detail, including the warnings every draft comes back with:
Drafts.