POST /v1/content/:containerId/approve
Flip a container's approval status to approved so it can be scheduled.
/v1/content/:containerId/approve- Auth
- Bearer
- Scope
- content:approve
Flips approvalStatus from pending to approved, stamps the reviewer, and — if the project is still counting down first-N-blocked posts — decrements the block counter. From the moment this call returns, the container is schedulable.
This call is what unblocks schedule-content when the project's approval policy requires review. Without approval, scheduling returns APPROVAL_REQUIRED.
Path parameters
containerIdstring (uuid)requiredThe container to approve.
Body
notestringoptionalFree-text note, max 1024 chars. Stored on the container for audit.
Request
curl -X POST https://api.layers.com/v1/content/{containerId}/approve \
-H "X-Api-Key: $LAYERS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "note": "Looks good. Shipping tomorrow 7am." }'const res = await fetch(
`https://api.layers.com/v1/content/${containerId}/approve`,
{
method: 'POST',
headers: {
'X-Api-Key': process.env.LAYERS_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ note: 'Approved by agent.' }),
},
);
const result = await res.json();import os, httpx
r = httpx.post(
f"https://api.layers.com/v1/content/{container_id}/approve",
headers={
"X-Api-Key": os.environ["LAYERS_API_KEY"],
"Content-Type": "application/json",
},
json={"note": "Approved."},
)
result = r.json()Responses
{
"id": "cnt_01HXZ9...",
"approvalStatus": "approved",
"approvedAt": "2026-04-18T09:40:12Z",
"approvedBy": "api_key_c2037bb9..."
}{
"id": "cnt_01HXZ9...",
"approvalStatus": "approved",
"approvedAt": "2026-04-18T09:40:12Z",
"approvedBy": "api_key_c2037bb9...",
"pendingSchedulePromotion": {
"status": "ok",
"scheduledPostIds": [
"sp_01HXZN4K5M6P7QRS8TUV9WXYZA"
]
}
}If a pendingSchedulePromotion block is present with status: "failed", approval still flipped — but the queued schedule did not promote (e.g. the original target social account was removed). Re-issue schedule to re-create the rows.
{
"error": {
"code": "CONFLICT",
"message": "Container is already approved.",
"requestId": "req_...",
"details": { "approvalStatus": "approved" }
}
}Notes
Approval is idempotent at the state level: if the container is already
approved, we return 409 CONFLICT rather than a silent no-op. That way
your logic knows whether this call flipped the bit or a prior one did.
- Who can approve. Any API key with the
content:approvescope. - First-N gate. Each approval while
currentBlockedCount < firstNPostsBlockedincrements the counter. Once it hits the threshold, the gate stays open for the project's lifetime. - Publishing consequence. Approving doesn't publish. It just removes the gate. Schedule or publish now to actually put it on a surface.
- Pending-schedule promotion. If
schedulewas called before approval, those targets were stashed on the container aspendingSchedulemetadata and returned withgateStatus: "blocked_on_approval". This call promotes them to livescheduled_postsrows atomically — thescheduledPostIdsyou already received fromschedulebecome the ids of the new rows. The response surfaces the outcome underpendingSchedulePromotion.
Errors
| Code | When |
|---|---|
CONFLICT | Container is already approved, rejected, or not reviewable. |
VALIDATION_FAILED | Container isn't completed yet. |
NOT_FOUND | Container id not in this org. |
FORBIDDEN_SCOPE | Key lacks content:approve. |