{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/juzpost/json-schema/juzpost-post-schema.json",
  "title": "Post",
  "description": "A post with its metadata and per-account delivery state.",
  "x-generated": "2026-11-08",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/juzpost-openapi.json#/components/schemas/Post",
  "type": "object",
  "required": [
    "id",
    "status",
    "type",
    "content",
    "mediaUrls",
    "coverR2Key",
    "scheduledFor",
    "metadata",
    "socialAccountIds",
    "targets",
    "createdAt",
    "updatedAt"
  ],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "The post id."
    },
    "status": {
      "type": "string",
      "description": "Coarse lifecycle summary. Prefer `targets` when you need to know the real state per\naccount.\n"
    },
    "type": {
      "$ref": "#/$defs/PostType"
    },
    "content": {
      "type": "string",
      "description": "The post body or caption."
    },
    "mediaUrls": {
      "type": "array",
      "description": "Storage keys of the post's media. Empty when there is none.",
      "items": {
        "type": "string",
        "description": "A storage key under your own prefix."
      }
    },
    "coverR2Key": {
      "type": [
        "string",
        "null"
      ],
      "description": "Storage key of the cover image, or null if none was set."
    },
    "scheduledFor": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time",
      "description": "The post-level scheduled fire time, or null if unscheduled."
    },
    "metadata": {
      "type": "object",
      "description": "The post's public metadata: `title`, `description` and `hashtags`, plus `source` and\n`client` when the post was created through the API or a connector. Other internal\nkeys are never returned.\n",
      "properties": {
        "title": {
          "type": "string",
          "description": "The post title, if one was set."
        },
        "description": {
          "type": "string",
          "description": "The long-form description, if one was set."
        },
        "hashtags": {
          "type": "array",
          "description": "Hashtags without the leading `#`, if any were set.",
          "items": {
            "type": "string",
            "description": "A single hashtag, without the `#`."
          }
        },
        "source": {
          "type": "string",
          "description": "`connector` when the post was created through the API or an MCP connector."
        },
        "client": {
          "type": "string",
          "description": "The API key or connector client that created the post."
        }
      }
    },
    "socialAccountIds": {
      "type": "array",
      "description": "Convenience list of the accounts this post fans out to, the same ids that appear in\n`targets`.\n",
      "items": {
        "type": "string",
        "format": "uuid",
        "description": "A social account id this post targets."
      }
    },
    "targets": {
      "type": "array",
      "description": "One entry per account the post fans out to. Empty until the post is scheduled.\n",
      "items": {
        "$ref": "#/$defs/PostTarget"
      }
    },
    "createdAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the post was created."
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "description": "When the post was last modified."
    }
  },
  "$defs": {
    "BlueskySettings": {
      "type": "object",
      "description": "Alt text for each file one Bluesky account publishes, and the content warning labels\non the post. Stored on that account's copy and returned on `GET /posts/{postId}`.\n",
      "required": [
        "altText",
        "labels"
      ],
      "properties": {
        "altText": {
          "type": "array",
          "maxItems": 4,
          "description": "One entry per file, in the order that account publishes them. Use an empty string\nfor a file with no description. The count must equal the number of files, or the\nrequest fails with `400`.\n",
          "items": {
            "type": "string",
            "description": "The description of one file, up to 2,000 characters counted as Bluesky counts them (graphemes)."
          }
        },
        "labels": {
          "type": "array",
          "maxItems": 2,
          "description": "Content warnings. At most one of `sexual` (Suggestive), `nudity` or `porn` (Adult),\nplus `graphic-media`. Only on posts with images or video. Bluesky leaves posts\nlabeled `sexual`, `porn` or `graphic-media` out of feeds for people who have not\nturned on adult content. Send `[]` for none.\n",
          "items": {
            "type": "string",
            "enum": [
              "sexual",
              "nudity",
              "porn",
              "graphic-media"
            ],
            "description": "One Bluesky self-label value."
          }
        }
      }
    },
    "PostTarget": {
      "type": "object",
      "description": "One account's copy of a post: its own status, fire time and failure reason. This is the\nauthoritative record of what happened for that account.\n",
      "required": [
        "socialAccountId",
        "platform",
        "username",
        "status",
        "publishAt",
        "publishedAt",
        "error",
        "link",
        "blueskySettings",
        "wordpressSettings"
      ],
      "properties": {
        "socialAccountId": {
          "type": [
            "string",
            "null"
          ],
          "format": "uuid",
          "description": "The social account this target publishes to."
        },
        "platform": {
          "type": [
            "string",
            "null"
          ],
          "description": "The account's platform id, e.g. `instagram`; null if the account was removed."
        },
        "username": {
          "type": [
            "string",
            "null"
          ],
          "description": "The account's handle on that platform; null if unknown or the account was removed."
        },
        "status": {
          "type": [
            "string",
            "null"
          ],
          "description": "Delivery state for this account, typically `pending`, `scheduled`, `uploading`,\n`published` or `failed`.\n"
        },
        "publishAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When this account's copy is due to fire, or null if not scheduled."
        },
        "publishedAt": {
          "type": [
            "string",
            "null"
          ],
          "format": "date-time",
          "description": "When this account's copy actually went live, or null if it has not."
        },
        "error": {
          "type": [
            "string",
            "null"
          ],
          "description": "Why this account's copy failed, or null if it has not failed."
        },
        "link": {
          "type": [
            "string",
            "null"
          ],
          "format": "uri",
          "description": "Where this account's copy lives on the platform, or null when it has not been\npublished or the platform gives no link. For a WordPress draft this is the wp-admin\nedit page, which opens only for a user signed in to that site.\n"
        },
        "blueskySettings": {
          "description": "Alt text and content warnings for a Bluesky account's copy, or null when none were\nset or the account is not on Bluesky.\n",
          "oneOf": [
            {
              "$ref": "#/$defs/BlueskySettings"
            },
            {
              "type": "null"
            }
          ]
        },
        "wordpressSettings": {
          "description": "Status, categories, tags, slug, excerpt, featured image alt text and SEO fields for a\nWordPress account's copy, or null when none were set or the account is not on\nWordPress.\n",
          "oneOf": [
            {
              "$ref": "#/$defs/WordPressSettings"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    },
    "PostType": {
      "type": "string",
      "description": "What kind of content the post carries. Anything other than `text` and `blog` must have at\nleast one media key before it can be scheduled. `blog` is a WordPress blog post: `title`\nis required and `description` is the body, HTML sent as written (plain text works, blank\nlines become paragraphs). The first image is the featured image and any others are added\nunder the body. Images only.\n",
      "enum": [
        "text",
        "image",
        "video",
        "story",
        "blog"
      ]
    },
    "WordPressSettings": {
      "type": "object",
      "description": "How one WordPress account's copy of a blog post is created. Every field is optional.\nStored on that account's copy and returned on `GET /posts/{postId}`.\n",
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "publish",
            "draft"
          ],
          "description": "`publish` (the default when omitted) or `draft`, which creates a WordPress draft for\nthe site's author to review.\n"
        },
        "categories": {
          "type": "array",
          "description": "Category names. Each must already exist on the site; an unknown name fails with\n`400` listing the site's categories.\n",
          "items": {
            "type": "string",
            "description": "One category name."
          }
        },
        "tags": {
          "type": "array",
          "description": "Tag names. A tag the site does not have yet is created.",
          "items": {
            "type": "string",
            "description": "One tag name."
          }
        },
        "slug": {
          "type": "string",
          "maxLength": 200,
          "description": "The URL slug. Made from the title when omitted."
        },
        "excerpt": {
          "type": "string",
          "description": "Short summary shown in feeds and link previews."
        },
        "featuredImageAlt": {
          "type": "string",
          "description": "Alt text for the featured image, which is the post's first image."
        },
        "seo": {
          "type": "object",
          "description": "Yoast SEO fields. Only for an account whose site lets it write them; otherwise the\nrequest fails with `400`.\n",
          "properties": {
            "title": {
              "type": "string",
              "description": "SEO title shown in search results instead of the post title."
            },
            "description": {
              "type": "string",
              "description": "Meta description. Yoast prints none when this is omitted."
            },
            "focusKeyphrase": {
              "type": "string",
              "description": "Yoast focus keyphrase."
            }
          }
        }
      }
    }
  }
}