Layers
Partner APIAPI referenceApproval

POST /v1/content/:containerId/approve

Flip a container's approval status to approved so it can be scheduled.

View as Markdown
POST/v1/content/:containerId/approve
Phase 1stable
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

  • containerId
    string (uuid)required
    The container to approve.

Body

Body (all optional)
  • note
    stringoptional
    Free-text note, max 1024 chars. Stored on the container for audit.

Request

terminal
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." }'
approve.ts
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();
approve.py
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

200Approved. The container is ready to schedule.
{
  "id": "cnt_01HXZ9...",
  "approvalStatus": "approved",
  "approvedAt": "2026-04-18T09:40:12Z",
  "approvedBy": "api_key_c2037bb9..."
}
200Approved AND a previously-blocked schedule was promoted to live scheduled_posts.
{
  "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.

409Container is already approved, rejected, or not in a reviewable state.
{
  "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:approve scope.
  • First-N gate. Each approval while currentBlockedCount < firstNPostsBlocked increments 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 schedule was called before approval, those targets were stashed on the container as pendingSchedule metadata and returned with gateStatus: "blocked_on_approval". This call promotes them to live scheduled_posts rows atomically — the scheduledPostIds you already received from schedule become the ids of the new rows. The response surfaces the outcome under pendingSchedulePromotion.

Errors

CodeWhen
CONFLICTContainer is already approved, rejected, or not reviewable.
VALIDATION_FAILEDContainer isn't completed yet.
NOT_FOUNDContainer id not in this org.
FORBIDDEN_SCOPEKey lacks content:approve.

See also

On this page