PPactDocs
Social

Composer

Draft one post, target multiple connected accounts, preview each channel against its real character and media limits, and rewrite the draft with your own Anthropic key — then save, schedule, or publish.

Composer

The composer is where a single idea becomes a post. You write the body once, pick which connected accounts should receive it, watch a live per-channel preview and character counter, optionally have AI tighten the copy, then save it as a draft, schedule it, or publish immediately. The page is wired to the live /v1/social API — it lists your accounts from GET /v1/social/accounts and creates posts through POST /v1/social/posts.

Live surface

Account selection, per-channel limit enforcement, draft/schedule/publish, and AI rewriting are all backed by real endpoints. The limits table is currently a client-side map (web/src/app/(app)/social/content-limits.ts) pending a content_limits field on the channel framework.

Per-channel limits

Because a 3,000-character LinkedIn post won't fit in a 280-character tweet, the composer shows a side-by-side preview per selected channel with a live counter and an over-limit warning. The current limits (from content-limits.ts):

ProviderChar limitMedia
linkedin3,0009 images / 1 video
facebook63,20610 images / 1 video
instagram2,20010-image carousel
threads50010 images / 1 video
twitter2804 images / 1 video / 1 GIF
tiktok2,2001 video
youtube5,0001 video
bluesky3004 images
mastodon5004 images / 1 video / 1 audio

Limits are a versioned client-side map

These figures reflect each network's public policy as of the map's last update. When the channel framework exposes a canonical content_limits field, the composer will read it live and this map is removed. Treat an over-limit warning as advisory — the provider is the final authority at publish time.

AI rewrite

The composer's rewrite actions call POST /v1/social/composer/ai-improve, which routes through the tenant's own Anthropic provider (BYOK). Modes (_MODE_INSTRUCTIONS in api/routes/social.py):

  • improve — tighten grammar and phrasing, keep the author's voice, length within ~±15%.
  • shorter — cut roughly 40%, drop filler, keep every concrete fact.
  • punchier — sharpen the hook and energy without inventing claims.
  • hashtags — leave the body unchanged, append 3–5 relevant hashtags.
  • first_line — rewrite only the opening sentence, keep every other line verbatim.
bash
curl -X POST https://api.pact.place/v1/social/composer/ai-improve \
  -H "Authorization: Bearer $PACT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"mode":"shorter","content":"Our Q3 release ships today...","provider":"linkedin"}'

If the tenant has no Anthropic key configured, the endpoint returns 409 ai_not_configured so the UI can render a "Connect AI provider" call to action rather than a generic error. The assistant is deliberately conservative: a contentless draft (a lone emoji, random characters) is returned unchanged rather than padded into a fabricated post, and the draft is fenced as data so text like "ignore your instructions" is treated as post copy, not a command.

Saving, scheduling, publishing

  1. 1

    Pick accounts

    Select one or more connected accounts. A disabled account rejects the post with 409 account_disabled.
  2. 2

    Write and preview

    Type the body; each selected channel renders a preview with its own counter.
  3. 3

    Choose an action

    Save as draft, set a scheduled_for time, or set publish_now: true. Scheduling and immediate publish are mutually exclusive (the API returns 400 if both are sent).
  4. 4

    Publish

    Publish immediately, or later via POST /v1/social/posts/{public_id}/publish. A provider error flips the post to failed with a failure_reason.

A scheduled post does not send itself yet

Setting scheduled_for records the intent and places the post on the calendar, but no worker currently publishes scheduled posts automatically at their due time. Publish scheduled posts explicitly until that background job ships.