My Multi-Platform Publishing Data Model

AveryDreamer Novice 9h ago 226 views 14 likes 2 min read

Treating a social media post as a simple string works until you try to hit more than one platform. When I started building OpenPost, I thought a source text and a list of accounts would be enough. It wasn't.

The reality is that "one post" is actually several different formats. X needs threads, LinkedIn takes PDFs, and Instagram requires carousels. Even two accounts on the same platform often need different copy. Instead of stacking endless exceptions into my code, I had to rethink the data model from scratch.

Separating Intent from Rendition

I shifted the architecture to separate the "Publication" (the intent) from the "Rendition" (the actual output).

The Publication holds the core idea and source text, while the Rendition is specific to the account and platform. Here is the simplified Go structure:

type Publication struct {
 ID string
 WorkspaceID string
 Title string
 ContentProfile string
 SourceText string
 SourceURL string
 Status string
 ScheduledAt time.Time
 ActualRunAt time.Time
}

And the corresponding rendition:

type Rendition struct {
 ID string
 PublicationID string
 SocialAccountID string
 Platform string
 Profile string
 Body string
 Title string
 Description string
 SettingsJSON string
 Status string
 ExternalID string
 ExternalURL string
 ErrorMessage string
}

Linking renditions to specific accounts rather than just platforms was a critical move. My personal LinkedIn and a company page are both "LinkedIn," but they rarely share the exact same copy. This also solves the media problem; a rendition can have its own alt text or thumbnail timestamps, which a flat list of attachments couldn't handle.

Validating the Output, Not the Source

A common trap in this AI workflow is validating the source text and assuming the final output is safe. That's a mistake. If an LLM agent modifies the rendition to fit a platform's style, it might accidentally exceed a character limit or use an unsupported image format.

I implemented content profiles (like short_text, thread, or carousel) to define the general shape. The provider adapter then performs a final check on the specific rendition before it ever hits the API.

Integrating LLM Agents via MCP

To make this work with AI agents, I needed a way to let them prepare drafts without giving them full administrative access to provider credentials. I implemented two MCP scopes:

  • mcp:read: Allows the agent to inspect workspace data.
  • mcp:full: Allows creating, editing, and publishing.

This is enforced server-side. If a client only has read access, the mutation executor is stripped during tool discovery, ensuring the agent can't accidentally trigger a post without permission. This setup makes the transition from "AI-generated draft" to "human-approved post" much cleaner.
WorkflowAI Implementationopensourceselfhostedgo

All Replies (3)

R
RayTinkerer Novice 9h ago
How are you handling platform-specific character limits in your schema?
0 Reply
J
JulesCrafter Novice 9h ago
Just use a headless CMS. Overcomplicating it with a custom model is a waste of time.
0 Reply
K
KaiDev Expert 9h ago
I learned this the hard way after my 280-character limit decided to eat my entire caption.
0 Reply

Write a Reply

Markdown supported