---
title: "Create scheduled post"
method: POST
path: "/api/apps/{app_id}/social-calendar/posts"
---

# Create scheduled post

`POST /api/apps/{app_id}/social-calendar/posts`

<Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Adds a post to the app's social calendar. The post starts as a `proposal`, which means it's on the calendar but nothing publishes it yet: approve it with [Approve scheduled posts](/api-reference/approve-scheduled-posts), then run [Start scheduling posts](/api-reference/start-scheduling-posts) to hand it to the publisher.

`instagram` and `linkedin` are the only platforms Base44 can publish to, so they're the only ones this endpoint accepts. The app needs a connected account for that platform with publishing permission by the time you schedule the post, not when you create it.

Set `scheduled_at` far enough ahead that it's still in the future when you schedule the post. Scheduling refuses an instant that has already passed and marks the post `failed`, and a failed post can't be revived. Send an offset-aware timestamp, or a naive one that Base44 reads as UTC. `scheduled_timezone` only decides the wall-clock time reported back in `scheduled_local_at`; it never moves the instant the post goes out.

The social calendar endpoints share two rate limits: 20 requests per minute across creating, editing, deleting and approving posts, and 40 requests per minute across the rest. This endpoint counts against the 20.

<Note>Only the fields documented here are accepted. Base44 owns the post's id, lifecycle status, plan lineage, workflow binding and publish results, and any other field in the body fails with a 422.</Note>

<Note>This endpoint accepts a personal API key. Workspace API keys are not authorized for it and are rejected with a 403.</Note>

## Path parameters

- `app_id` string, required — ID of the app whose social calendar you want.

## Request body

- CreateScheduledPostPayload — Content and schedule only. Ownership, lifecycle status, workflow binding, plan lineage and publish results are server-owned; sending them is an error rather than a silently ignored field.
  - `platform` 'x' | 'instagram' | 'tiktok' | 'linkedin' | 'reddit' | 'facebook', required
  - `title` string, required — Title of the post, which labels it on the calendar. It isn't published as text, except on LinkedIn, where a post with an image sends it as the image's title.
  - `body` string, required — Body text of the post. Base44 publishes this followed by `hashtags`, so leave the tags out of it.
  - `scheduled_at` string, date-time, required — When the post should publish, as an ISO 8601 timestamp between the years 2000 and 2100. Send an offset, or a naive timestamp that Base44 reads as UTC. It has to still be in the future when you schedule the post.
  - `scheduled_timezone` string — IANA timezone name used to render `scheduled_local_at` in the response. It never moves the instant the post publishes. Defaults to `UTC`.
  - `hook` string — The angle the post leads with, for your own reference on the calendar. Empty by default.
  - `best_time_reason` string — Why you picked this time, shown alongside the post on the calendar. Empty by default.
  - `hashtags` string[] — Up to 30 hashtags, published after `body`. Send them without the leading `#`; one you include is stripped before publishing.
  - `image_url` string, nullable — HTTPS URL of the image to publish with the post. Required for an Instagram post, which is rejected at publish time without one, and optional on LinkedIn.
  - `image_prompt` string, nullable — Prompt the image came from, kept for reference. This endpoint doesn't generate an image from it.

## Response `201`

The created post.

- ScheduledPostResponse
  - `id` string, required — ID of the post. Pass it as `post_id` to the other social calendar endpoints.
  - `app_id` string, required — ID of the app the post belongs to.
  - `plan_id` string, nullable, required — ID of the content plan the post was generated from, or `null` for a post created through [Create scheduled post](/api-reference/create-scheduled-post).
  - `source_post_id` string, nullable, required — ID this post has inside the content plan it came from, or `null` for a post created through the API.
  - `platform` string, required — Account the post publishes to. Base44 publishes to `instagram` and `linkedin`. A post generated from a content plan can also name `x`, `tiktok`, `reddit` or `facebook`, which Base44 plans for but can't publish, and scheduling such a post fails it.
  - `title` string, required — Title of the post. It labels the post on the calendar and isn't published as text, except on LinkedIn, where a post with an image sends it as the image's title.
  - `body` string, required — Body text of the post. Base44 publishes this followed by `hashtags`, so leave the tags out of it.
  - `hook` string, required — The angle the post leads with. A plan-generated post carries the angle the planner picked, such as `pain_point` or `social_proof`; a post you create carries whatever you sent, or an empty string.
  - `cover_index` integer, required — Zero-based position of the post in the series its content plan generated, which is how the calendar picks its cover image. Always `0` for a post created through the API.
  - `scheduled_at` string, date-time, required — When the post publishes, always in UTC. A post read back from the calendar carries no offset (`2026-09-15T14:00:00`), while the one [Create scheduled post](/api-reference/create-scheduled-post) returns carries `+00:00`. Read both as UTC.
  - `scheduled_local_at` string, required — The same instant as `scheduled_at`, rendered in `scheduled_timezone` as an ISO 8601 timestamp. Display only.
  - `scheduled_timezone` string, required — IANA timezone `scheduled_local_at` is rendered in. It never moves the instant the post publishes.
  - `best_time_reason` string, required — Why this time was picked, written by the planner for a plan-generated post. Empty unless something set it.
  - `hashtags` string[], required — Hashtags published after `body`, without the leading `#`. A leading `#` you send is stripped before publishing.
  - `image_url` string, nullable, required — HTTPS URL of the image published with the post, or `null` if it has none. An Instagram post needs one to publish.
  - `image_prompt` string, nullable, required — Prompt the post's image was generated from, or `null` if there is none. Kept for reference; this endpoint doesn't generate images from it.
  - `status` string, required — Where the post is in its lifecycle: `proposal` before you approve it, `scheduled` once approved, `publishing` while it's going out, then `posted`. `needs_reconnect` means the platform account has to be reconnected, `not_materialized_plan_limit` that the workspace plan doesn't cover publishing, `publish_outcome_unknown` that the platform may have accepted the post but Base44 couldn't confirm it, and `failed` that the post can't go out.
  - `workflow_id` string, nullable, required — ID of the automation that publishes this post, set once [Start scheduling posts](/api-reference/start-scheduling-posts) hands it over, and `null` before that. Its presence is what tells you the publish time is fixed.

## Other responses

- `401` — Missing or invalid credentials.
- `403` — You don't have editor access to this app, or you used a workspace API key.
- `404` — App not found, or the social calendar is not enabled for your account.
- `422` — Validation Error
- `429` — Rate limit exceeded (20 requests per minute).

---

[API](https://skmtc.dev/base44/apis/base44-app-management-api.md) · [All operations](https://skmtc.dev/base44/apis/base44-app-management-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/base44/base44-app-management-api/revisions/173e4e9c63c2/schema)
