POST /v1/content/:containerId/schedule
Schedule a content container to one or more connected accounts at a specific time. The approval gate can block unapproved content.
/v1/content/:containerId/schedule- Auth
- Bearer
- Scope
- publish:write
Schedule a completed content container to publish to N social accounts at a specific time. Each target gets its own scheduledPostId you can later reschedule or cancel.
Nothing publishes until the container's approval flag is set. If the project has requires_approval: true and the container is still approvalStatus: "pending", this call returns 202 with gateStatus: "blocked_on_approval" and stashes the intent on the container — a subsequent approve promotes it into scheduled_posts automatically. If approvalStatus is rejected, the call returns 409 CONTENT_REJECTED. See Approval.
The container must be status: "completed" — you can't schedule something that's still generating.
containerIdstringrequiredCompleted content container id.
Idempotency-Keystring (UUID)optionalSame key + same body replays the cached response for 24 hours. Recommended.
scheduledForstring (ISO-8601)requiredWhen to publish. Schedule at least 30 seconds in the future; past/near-now values are accepted but the dispatcher may publish immediately on the next tick.targetsTarget[]requiredOne entry per destination account. At least one required.
socialAccountIdstringrequiredAccount id from list-social-accounts.modestringrequiredPublish mode. Not every mode is valid on every platform — see table below.One of:direct_publish,draft_to_device,reels,feedscheduledPostIdstringoptionalPartner-created id for this post. Server generates one if omitted.captionOverridestringoptionalReplace the container's caption for this target only.firstCommentOverridestringoptionalReplace the container's first comment for this target only.
Valid mode per platform
| Platform | Modes |
|---|---|
| TikTok | direct_publish, draft_to_device |
reels, feed, draft_to_device |
draft_to_device sends the media to the logged-in mobile app as a draft — the end-customer finishes posting by hand. Useful when platform ToS prefers a human tap.
The schedule request currently accepts any mode string per the enum; platform compatibility is enforced at publish time, not at schedule time. Sending a platform-incompatible mode will queue the post but fail during publish with a platform error. Strict schedule-time validation is planned.
Example request
curl https://api.layers.com/v1/content/cnt_01HXZM3K4N5P6QRS7TUV8WXYZ9/schedule \
-H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..." \
-H "Idempotency-Key: 8f1d6c3e-4b2a-4a18-9e4f-c2d7a1b0e999" \
-H "Content-Type: application/json" \
-d '{
"scheduledFor": "2026-04-19T14:00:00Z",
"targets": [
{ "socialAccountId": "sa_01HXZ9P2M4N5KLM6TUV7WXYZ9A", "mode": "direct_publish" },
{ "socialAccountId": "sa_01HXZQ8N3C5R6STUV7WXYZ9AB", "mode": "reels", "captionOverride": "Fresh pour, ready in 30s." }
]
}'const result = await layers.publishing.schedule(
{
containerId: "cnt_01HXZM3K4N5P6QRS7TUV8WXYZ9",
scheduledFor: "2026-04-19T14:00:00Z",
targets: [
{ socialAccountId: "sa_01HXZ9P2M4N5KLM6TUV7WXYZ9A", mode: "direct_publish" },
{
socialAccountId: "sa_01HXZQ8N3C5R6STUV7WXYZ9AB",
mode: "reels",
captionOverride: "Fresh pour, ready in 30s.",
},
],
},
{ idempotencyKey: crypto.randomUUID() },
);
if (result.gateStatus === "blocked_on_approval") {
// Approve the container or leave the posts queued; they flip to queued once approved.
}result = layers.publishing.schedule(
container_id="cnt_01HXZM3K4N5P6QRS7TUV8WXYZ9",
scheduled_for="2026-04-19T14:00:00Z",
targets=[
{"socialAccountId": "sa_01HXZ9P2M4N5KLM6TUV7WXYZ9A", "mode": "direct_publish"},
{"socialAccountId": "sa_01HXZQ8N3C5R6STUV7WXYZ9AB", "mode": "reels",
"captionOverride": "Fresh pour, ready in 30s."},
],
idempotency_key=str(uuid.uuid4()),
)Response
{
"scheduledPostIds": [
"sp_01HXZN4K5M6P7QRS8TUV9WXYZA",
"sp_01HXZN4K5M6P7QRS8TUV9WXYZB"
],
"gateStatus": "queued",
"scheduledFor": "2026-04-19T14:00:00Z"
}{
"scheduledPostIds": [
"sp_01HXZN4K5M6P7QRS8TUV9WXYZA",
"sp_01HXZN4K5M6P7QRS8TUV9WXYZB"
],
"gateStatus": "blocked_on_approval",
"scheduledFor": "2026-04-19T14:00:00Z"
}gateStatus: "queued" (200) means scheduled_posts rows exist now and will attempt to publish at scheduledFor. gateStatus: "blocked_on_approval" (202) means the request was accepted but the rows do not yet exist — the intent is stashed on the container's pendingSchedule metadata; a subsequent approve call promotes those intents into live scheduled_posts rows. The scheduledPostIds are the stable ids those rows will adopt on promotion, so it's safe to record them on your side immediately.
Errors
| Status | Code | When |
|---|---|---|
| 422 | VALIDATION | Empty targets, malformed scheduledFor, or bad enum value. |
| 401 | UNAUTHENTICATED | Missing or invalid key. |
| 403 | FORBIDDEN_SCOPE | Key lacks publish:write. |
| 404 | NOT_FOUND | Container or a target account not in your organization. |
| 409 | CONFLICT | Container not completed, or a scheduledPostId you created already exists with a different body. |
| 409 | CONTENT_REJECTED | Container's approval_status is rejected. Regenerate or replace the container. |
| 429 | RATE_LIMITED | Write budget exhausted. |
See also
POST /v1/content/:id/publish— publish nowGET /v1/scheduled-posts/:id— poll statusPOST /v1/content/:id/approve— release the gate- Approval — how the gate works