Skip to content

Content model

Every publishable unit in NativPost is a content object. Whether it lands on X as a text post, on Instagram as a Reel, or on YouTube as a Short, the same underlying record drives it.

{
"id": "9f2c8e7b-1a3d-4f60-b2c1-7e5a9d3c4b81",
"object": "content",
"caption": "Ship faster with NativPost.",
"hashtags": ["saas", "buildinpublic"],
"content_type": "reel",
"topic": "Launch week",
"media_urls": ["https://res.cloudinary.com/nativpost/video/upload/v1234/example.mp4"],
"target_platforms": ["instagram", "tiktok"],
"platform_specific": {},
"status": "scheduled",
"scheduled_for": "2026-07-19T13:00:00Z",
"published_at": null,
"aspect_ratio": "9:16",
"duration_seconds": 12.5,
"campaign_id": null,
"created_at": "2026-07-19T12:04:11Z",
"updated_at": "2026-07-19T12:07:52Z"
}

That is the complete object — the serializer maps fields explicitly, so nothing else appears. Ids are plain UUIDs with no type prefix; object is what tells you the resource kind.

FieldTypeNotes
idstring (UUID)
objectstringAlways "content".
captionstringThe post copy. Required on create, 1 to 4000 characters.
hashtagsstring[]Without the leading #. Max 50. Defaults to [].
content_typestringSee Content types. Defaults to single_image.
topicstring | nullFree-text grouping label, up to 280 characters.
media_urlsstring[]Absolute URLs, in order. Max 20. Defaults to [].
target_platformsstring[]Destinations. Max 10. Defaults to []. See Platforms.
platform_specificobjectPer-platform overrides keyed by platform id. Defaults to {}.
statusstringSee Statuses. Defaults to draft.
scheduled_forstring | nullISO 8601. When the publish cron should pick it up.
published_atstring | nullISO 8601. Set by the publish cron.
aspect_ratiostring | nullFor example 9:16, 1:1.
duration_secondsnumber | nullVideo length. Set by the engine, not by clients.
campaign_idstring | nullUUID of the owning campaign, if any.
created_atstringISO 8601.
updated_atstringISO 8601.

There is no workspace_id on the object. The API key already scopes every request to one workspace, so the field would be redundant.

  • Identity: id, object, campaign_id.
  • Lifecycle: status, scheduled_for, published_at, created_at, updated_at.
  • Copy: caption, hashtags, topic.
  • Rendering: content_type, media_urls, aspect_ratio, duration_seconds.
  • Targeting: target_platforms plus per-platform overrides in platform_specific.

There is no separate body_text, hook_text, or slide_copy. All post copy goes in caption; hashtags are split out into their own array so the pipeline can place them per platform. Where a network needs different wording, override it in platform_specific rather than creating a second content object.

media_urls holds absolute URLs, not media-asset ids. Browse the media library and copy the url off each asset. Rendered video and image output produced by the engine is written back into the same field.

A content object can exist on its own or belong to a campaign via campaign_id. Campaigns define a plan — cadence, length, content mix — and generate child content asynchronously after launch. campaign_id is set by the generator; it is not a field you can pass on create or update. See Campaigns.

The API does not freeze fields once status reaches published — a PATCH will still be accepted and will still update the stored record. It just has no effect on the live post: the platform already has its own copy. To change what is live, edit it in the platform’s own interface.

Note that PATCH will not accept published as a status value. That transition belongs to the publish cron.