{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/hermoso-ai/json-schema/hermoso-ai-post-create-schema.json",
  "title": "PostCreate",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/app_hermoso_ai_rest.json#/components/schemas/PostCreate",
  "type": "object",
  "required": [
    "channels"
  ],
  "properties": {
    "channels": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "One or more channel ids from GET /v1/channels. DELIBERATELY NOT AN ENUM — the set is derived from what the product can publish to, and it GROWS as platform approvals land. Read it from GET /v1/channels rather than hard-coding it, and treat an id you have not seen as valid."
    },
    "caption": {
      "type": "string",
      "description": "The post text. Per-channel length limits are enforced at create time, not discovered at publish time."
    },
    "title": {
      "type": "string",
      "description": "The headline Pinterest and YouTube each require. Derived from the caption when omitted."
    },
    "link": {
      "type": "string"
    },
    "media": {
      "type": "array",
      "description": "Ordered. One item is a single post; two or more are a carousel, in the order given. Each item is a Hermoso-hosted URL from POST /v1/media, as a bare string or as {url, type}.",
      "items": {
        "oneOf": [
          {
            "type": "string"
          },
          {
            "type": "object",
            "properties": {
              "url": {
                "type": "string"
              },
              "type": {
                "type": "string",
                "enum": [
                  "image",
                  "video"
                ]
              }
            }
          }
        ]
      }
    },
    "scheduled_at": {
      "type": "string",
      "format": "date-time",
      "description": "Omit to publish immediately. Otherwise ISO-8601, at least 60 seconds out and within the documented horizon."
    },
    "use_queue": {
      "type": "boolean",
      "description": "Take the next free slot in this profile’s posting schedule instead of naming a time. Mutually exclusive with scheduled_at — sending both is an error, never a silent precedence."
    },
    "timezone": {
      "type": "string",
      "description": "IANA zone for use_queue. Defaults to the profile’s saved zone."
    },
    "visibility": {
      "type": "string",
      "description": "public (default) | unlisted | private | draft. Refused at create time where a channel cannot honour it, never silently downgraded at publish time."
    },
    "visibility_by_channel": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      }
    },
    "captions_by_channel": {
      "type": "object",
      "additionalProperties": {
        "type": "string"
      },
      "description": "Override the caption for one channel."
    },
    "targets": {
      "type": "object",
      "additionalProperties": true,
      "description": "Which account to post to, where the connection holds more than one. Omit for the default. GET /v1/channels names the field each channel uses."
    },
    "options": {
      "type": "object",
      "additionalProperties": true,
      "description": "Per-channel publish metadata (YouTube description, tags, categoryId, madeForKids, notifySubscribers, paidProductPlacement, brandPartner and a captionTrack {url|text, language, name} uploaded right after the video; X thread parts, Instagram alt text, and so on)."
    },
    "first_comment": {
      "type": "string",
      "description": "A comment the account posts on its own post right after it publishes: Facebook Pages, Instagram, and LinkedIn company Pages (LinkedIn needs its comments authorization, provider linkedin_community). Up to 2,000 characters, refused (never cut) when longer. Other channels publish the post without it and say so on their result. A comment that fails never undoes the post; its outcome is reported on the channel’s result."
    }
  }
}