{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/fuxux-com/json-schema/fuxux-com-post-create-schema.json",
  "title": "PostCreate",
  "description": "`captions` is required unless `draft` is `true`, in which case `prompt` or at least one caption must be present instead.",
  "x-generated": "2026-11-09",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/fuxux-com-openapi.json#/components/schemas/PostCreate",
  "type": "object",
  "required": [
    "captions"
  ],
  "properties": {
    "prompt": {
      "type": "string",
      "maxLength": 500,
      "description": "Optional idea stored on the post"
    },
    "captions": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Map of platform id → caption. Platform ids: `twitter`, `instagram`, `linkedin`, `facebook`, `tiktok`, `youtube`, `bluesky`, `threads`, `pinterest`, `discord`, `kick`, `twitch`, `telegram`, `slack`. An unknown platform key, a caption that is not text, and (without `targets`) a platform with no connected account do not stop the post: each is listed in the answer's `skipped`. Without `targets`, each captioned platform posts to its first connected account. Reddit is not available through the API: Reddit posts are created in the Fuxux web app, and a `reddit` caption or target is refused with 403 `reddit_web_only`. Google Business Profile is coming soon and can't be connected or posted to yet."
    },
    "targets": {
      "type": "array",
      "items": {
        "$ref": "#/$defs/PostTarget"
      },
      "description": "Optional: exactly which connected accounts to post to, with per-platform options. Each target needs a caption for its platform in `captions`. Needed for picking between several accounts on one platform. A target whose platform has no caption, and a caption with no target on its platform, are left out and listed in `skipped`."
    },
    "scheduled_at": {
      "type": "string",
      "format": "date-time",
      "description": "If set, post is scheduled (must be in the future, with a time zone: Z or an offset such as +01:00). If omitted, the app publishes immediately. Mutually exclusive with `draft`."
    },
    "media_urls": {
      "type": "array",
      "items": {
        "type": "string",
        "format": "uri"
      },
      "maxItems": 4,
      "description": "Public URLs from `POST /api/media/upload`. At most 4 are used. An entry that is not a public https URL, or past the fourth, is left out and listed in `skipped`; if none can be used, the request is refused with 400."
    },
    "draft": {
      "type": "boolean",
      "description": "If `true`, saves the post as a draft instead of scheduling or publishing it. `captions` is not required when `draft` is `true`, but `prompt` or at least one caption must be present. Mutually exclusive with `scheduled_at`."
    }
  },
  "$defs": {
    "PostTarget": {
      "type": "object",
      "required": [
        "account_id",
        "platform"
      ],
      "properties": {
        "account_id": {
          "type": "string",
          "format": "uuid",
          "description": "Connected account id from `GET /api/accounts`"
        },
        "platform": {
          "type": "string",
          "enum": [
            "twitter",
            "instagram",
            "linkedin",
            "facebook",
            "tiktok",
            "youtube",
            "bluesky",
            "threads",
            "pinterest",
            "discord",
            "kick",
            "twitch",
            "telegram",
            "slack"
          ],
          "description": "Must match the account's platform"
        },
        "format": {
          "type": "string",
          "enum": [
            "feed",
            "story"
          ],
          "description": "Instagram and Facebook only; defaults to `feed`"
        },
        "boardId": {
          "type": "string",
          "description": "Pinterest only: id of the board to pin to. If omitted, the pin goes to the account's first public board."
        },
        "tiktokOptions": {
          "type": "object",
          "description": "TikTok only. `mode` `inbox` (default) sends the video to the creator's TikTok inbox to finish; `direct` publishes it (needs Direct Post access on the account).",
          "properties": {
            "mode": {
              "type": "string",
              "enum": [
                "inbox",
                "direct"
              ]
            },
            "privacyLevel": {
              "type": "string",
              "description": "One of the privacy levels TikTok offers the account (for example `SELF_ONLY`)"
            },
            "disableComment": {
              "type": "boolean"
            },
            "disableDuet": {
              "type": "boolean"
            },
            "disableStitch": {
              "type": "boolean"
            },
            "brandOrganic": {
              "type": "boolean",
              "description": "Promotional content (your own brand)"
            },
            "brandContent": {
              "type": "boolean",
              "description": "Paid partnership (branded content); can't be private"
            }
          }
        },
        "category": {
          "type": "object",
          "description": "Twitch and Kick only: stream category set with the title (Twitch game id or Kick category id)",
          "required": [
            "id",
            "name"
          ],
          "properties": {
            "id": {
              "type": "string"
            },
            "name": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}