Layers

POST /v1/content/:containerId/publish

Publish a container immediately.

View as Markdown
POST/v1/content/:containerId/publish
Phase 1stableidempotent
Auth
Bearer
Scope
publish:write

Publish now. If you need a pre-publish cancel window, use schedule instead and cancel the scheduled post before it starts publishing.

Nothing publishes until the container's approval flag is set. If the project has requires_approval: true and the container is not yet approved, this call returns 403 APPROVAL_REQUIRED - flipping approval later will still publish, but at that later time, not immediately. Unlike schedule, publish does not stash a pending intent — partners should wait for approval and re-issue. See Approval.

Atomic batch semantics. Up to 50 targets per call. Identical all-or-nothing behavior to schedule — the first invalid socialAccountId aborts the whole call with no rows written.

Path
  • containerId
    stringrequired
    Completed content container id.
Headers
  • Idempotency-Key
    string (UUID)optional
    Same key + same body replays the cached response. Especially important here - accidental retries double-post.
Body
  • targets
    Target[]required
    Same shape as schedule. At least one required, max 50. All-or-nothing.

Target shape is identical to /schedule. All three modes (publish, draft, managed) are supported on this endpoint with the same per-target fields.

Target
  • socialAccountId
    stringrequired
    Account id from list-social-accounts. Must already be connected to the same project as the container.
  • mode
    stringrequired
    How the post is delivered. See "Modes" below.
    One of: publish, draft, managed
  • captionOverride
    stringoptional
    Replace the container's caption for this target only. Up to 4000 chars. The publisher reads this in preference to `content_containers.caption`.
  • firstCommentOverride
    stringoptional
    Replace the container's first comment for this target only. Up to 4000 chars.
  • tiktokPostSettings
    objectoptional
    TikTok-only knobs. See "TikTok post settings" below. Ignored on `instagram` targets and on `managed` mode.
  • tiktokMusic
    objectoptional
    TikTok music selection for image / slideshow posts. See "TikTok music" below. Ignored on `instagram` targets. **Rejected with `422 VALIDATION`** when set on a `draft` target (TikTok inbox drafts have no music slot — the creator picks audio on-device). Omit on draft.
  • shareReelToFeed
    booleanoptional
    Instagram Reels placement (mirrors Meta's Graph API `share_to_feed`). `true` (default) — the Reel appears in the Reels tab AND on the main profile grid. `false` — Reels tab only, skip the grid. **Rejected with `422 VALIDATION`** when set on (a) non-Instagram targets, (b) non-video content containers (slideshow / single-image), or (c) any mode other than `publish`. See "Instagram Reels placement" below.

Modes

ModeWhat it does
publishLayers posts to the connected platform automatically. Row lands at published (with externalId + externalUrl populated) on success, or failed (with lastError populated, no retry) on a hard publish failure.
draftPushes the media as a draft to the user's mobile app — TikTok inbox or Instagram SMS. The end-user finishes posting from their phone. On success the polled status flips to draft (terminal from Layers' side — the creator finalizes on-device, invisible to us). On a platform handoff failure the row lands at failed with lastError populated and no retry; the wire enum is invariant — draft always means "delivered to device", failed always means "publisher tried and couldn't deliver."
managedDispatches via the project's connected managed-distribution provider. Requires the social account to belong to a layer with template_id = af058068-ad85-4fb8-92a1-f531b40bfcbc (managed distribution) and the layer's config.provider set.

IG placement (Reels vs feed) is determined by the container's media_type (image → feed, video → reels, multi → carousel). There is no feed/reels mode on the partner API — the publisher picks the placement automatically.

Instagram requires a Business or Creator account. Instagram Graph API publishing is not available on Personal accounts. A publish target pointed at a Personal-tier IG account lands at failed with lastError carrying the Graph API rejection message. Switch the account to Creator (free) in the Instagram mobile app's Account type and tools settings and re-issue.

externalUrl for Instagram. Instagram permalinks use an opaque shortcode (/p/<shortcode> or /reel/<shortcode>) that can't be derived from the numeric externalId alone. The publisher captures the permalink from the Graph API's ?fields=permalink after publish and surfaces it on externalUrl. A network blip on that follow-up fetch leaves externalUrl: null even though externalId and publishedAt are populated and the post is live — open the IG mobile app for the connected account to view it manually.

TikTok-only fields on Instagram targets. tiktokPostSettings and tiktokMusic are silently ignored on platform: "instagram" targets — they don't map to any Instagram Graph API knob. If you're building a generic publish helper that always sets these fields, expect them to be no-ops for IG; they're not a 422.

draft for Instagram requires the organization's primary operator to have a verified phone number; the SMS draft is sent to that operator. Without a verified phone the publisher's auto-publish loop falls back and the wire status lands at failed.

Instagram Reels placement

Videos posted to Instagram via publish go to Reels by default. Reels are short-form vertical videos and appear in two places on the connected account:

Where it showsWhen
Reels tab on the profileAlways — every Reel lands here
Main profile grid (with a play-icon overlay)When shareReelToFeed is omitted or true (Graph API default)
Only the Reels tab, not the main gridWhen shareReelToFeed: false

shareReelToFeed maps verbatim to Meta's share_to_feed parameter on the Reels media container.

Media typePlacementshareReelToFeed allowed?
Single videoReel + grid (default) or Reels tab only (false)✅ Yes
Slideshow / carousel (multiple images)Grid only — there is no Reels analog for carousels❌ No — 422 VALIDATION
Single imageGrid only — there is no Reels analog for single images❌ No — 422 VALIDATION

Slideshow and single-image Instagram posts are grid-only. Setting shareReelToFeed on a non-video container returns 422 VALIDATION (details.reason: "non_video_container"). The Graph API has no share_to_feed analog for CAROUSEL / IMAGE containers — placement is implicit.

TikTok post settings

tiktokPostSettings mirrors the UI's "Advanced settings" panel for TikTok. All fields are optional; missing fields fall back to TikTok's creator_info defaults (typically the most permissive privacy + interactions enabled).

FieldTypeNotes
privacyLevel"PUBLIC_TO_EVERYONE" | "MUTUAL_FOLLOW_FRIENDS" | "FOLLOWER_OF_CREATOR" | "SELF_ONLY"Required by TikTok for direct posts. The publisher defaults to PUBLIC_TO_EVERYONE when omitted.
disableCommentbooleanDisable comments on the published post.
disableDuetbooleanDisable Duet (videos only).
disableStitchbooleanDisable Stitch (videos only).
isBrandOrganicboolean"Your Brand" toggle — labels the post as Promotional content.
isBrandedContentboolean"Branded Content" toggle — labels the post as Paid Partnership. Required by TikTok ToS / FTC §255 when a creator is paid by a brand to post. Partners running paid promotions must set this.

TikTok music

tiktokMusic mirrors the UI's "Background Music" panel for TikTok image / slideshow posts. Applies to publish and managed only — TikTok inbox drafts (draft) cannot stamp music since the creator picks it on-device, and the schema rejects the field on draft targets with 422 VALIDATION (rather than silently dropping it).

FieldTypeNotes
mode"none" | "auto" | "manual"Required. none opts out of music. auto lets TikTok auto-suggest a trending sound. manual uses trackId.
trackIdstring (UUID)Required when mode: "manual". Track id from GET /v1/tiktok-music. 422 if the id isn't in the catalog.
"tiktokMusic": { "mode": "auto" }
"tiktokMusic": { "mode": "manual", "trackId": "8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999" }

manual on publish currently degrades to auto. TikTok's photo-publish API requires a Commercial Music Library id, which the trending-music catalog does not surface — manual and auto produce the same auto_add_music: true payload on direct publish. This matches the Layers UI's behavior. Managed-distribution targets DO honor the manual track: the publisher receives a resolved tiktokMusicLink and uses it verbatim. Use managed mode if you need exact track selection on TikTok image posts.

Two device-handoff paths. mode: "draft" here pushes a draft to the platform-native inbox (TikTok inbox / Instagram SMS draft) immediately. For the UI's "Text me this post" / Elle iMessage flow — sending media + caption + posting instructions to an arbitrary phone number, not to the connected creator account — call POST /v1/content/:containerId/notify-device.

Example request

curl https://api.layers.com/v1/content/cnt_8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999/publish \
  -H "Authorization: Bearer lp_..." \
  -H "Idempotency-Key: 3a7b2f11-9e4c-4a12-9d8a-b5c7e2f0a111" \
  -H "Content-Type: application/json" \
  -d '{
    "targets": [
      { "socialAccountId": "sa_71b2a4e5-8c3f-4d1a-9e7b-2c5d8f0a1b22", "mode": "publish" }
    ]
  }'
const result = await layers.publishing.publishNow(
  {
    containerId: "cnt_8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999",
    targets: [
      { socialAccountId: "sa_71b2a4e5-8c3f-4d1a-9e7b-2c5d8f0a1b22", mode: "publish" },
    ],
  },
  { idempotencyKey: crypto.randomUUID() },
);
result = layers.publishing.publish_now(
    container_id="cnt_8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999",
    targets=[
        {"socialAccountId": "sa_71b2a4e5-8c3f-4d1a-9e7b-2c5d8f0a1b22", "mode": "publish"},
    ],
    idempotency_key=str(uuid.uuid4()),
)

Response

200Queued for immediate publish
{
  "scheduledPostIds": ["sp_4c8e7d2f-9a1b-4c3d-8e7f-2a1b3c4d5e60"]
}

The response shape differs from /schedule/publish omits both gateStatus and scheduledFor. Pending containers 403 outright (so the only successful state is "queued for immediate publish" and gateStatus would always be "queued"), and scheduledFor would always echo now() — partners that need timestamps poll GET /v1/scheduled-posts/:id and read attemptedAt / publishedAt.

Errors

StatusCodeWhen
422VALIDATIONEmpty targets, invalid mode, or schema-level cross-field violations (e.g. tiktokMusic set on a draft target, or shareReelToFeed set on a non-Instagram target / non-video container / non-publish mode — see callouts above). When raised by shareReelToFeed, details.reason is one of "non_instagram_target" (also includes details.platform and details.socialAccountId) or "non_video_container" (also includes details.mediaType).
422SOCIAL_ACCOUNT_NOT_PUBLISH_READYA target's social account is connected to the project but isn't yet provisioned to publish in the requested mode. details.socialAccountId and details.mode identify the offending target. Verify the social account's setup for this project before retrying.
403APPROVAL_REQUIREDContainer not approved and project still in the approval window. Publish does not stash intent — re-issue once approved.
404NOT_FOUNDContainer or any target's account not in your organization (all-or-nothing).
409CONFLICTContainer not completed.
409CONFLICT_DUPLICATE_IDA partner-supplied scheduledPostId already exists. Pick a fresh sp_<uuid> and retry — same id can't be reused across calls (DB unique constraint, not a replay key; pair with Idempotency-Key if you need request-level replay).
409CONTENT_REJECTEDContainer's approval_status is rejected. Regenerate or replace the container.

See also

On this page