Content review
How the content-review gate works, what its three modes do, where it's enforced, and how to read the queue.
Content review is the checkpoint between "content generated" and "content leaves the building." It's a flag on each content item and a policy on each project. The API enforces the gate; you build the review UX.
The policy — three modes
Every project has a content-review policy with one of three values:
policy | What it does |
|---|---|
auto_approve | Generated content is publish-ready immediately. No human in the loop. |
review_first_n | First N items require human approval. Once N have cleared review, every subsequent item auto-approves. Uses firstN to pick N. |
review_all | Every item requires human approval, forever. |
Pick auto_approve for full autonomy, review_first_n to validate the first batch from a new project / influencer / brand brief and then let the optimizer fly, and review_all for regulated verticals where every post must be human-checked before it ships.
firstN is required when policy is review_first_n and forbidden otherwise — the API returns 422 VALIDATION on either violation.
The gate
Two things decide whether a content item can be scheduled:
- Project policy. Set on the project's content-review policy resource (above).
- Content status. Every content item has an
approvalStatusofnot_required|pending|approved|rejected. New containers on areview_*project start atpending; the rest default tonot_required.
The gate triggers on POST /v1/content/:containerId/schedule (and on /publish, which is just "schedule with scheduledFor: now"). If the container is pending, the schedule call is refused with 403 APPROVAL_REQUIRED until the container flips to approved. If the container is rejected, the call is refused with 409 CONTENT_REJECTED permanently.
approvalStatus === 'pending' → 403 APPROVAL_REQUIRED on schedule
approvalStatus === 'rejected' → 409 CONTENT_REJECTED on scheduleApproval is enforced at the schedule endpoint and at the just-before-publish check. Use the schedule endpoint for any publish flow that should respect the gate.
pendingCount is the queue depth
The policy resource also reports a server-computed pendingCount — the number of containers in this project that are currently sitting in approvalStatus: "pending". Read-only, useful for rendering "N items awaiting review" in your UI.
For review_first_n, the gate self-disables once the project has seen firstN containers move out of pending (any terminal state — approved, rejected — counts). Subsequent generations land at not_required automatically. This is checked at content-creation time, not at schedule time.
Approving and rejecting
POST /v1/content/:containerId/approve
{
"note": "On-brand, clean caption"
}
POST /v1/content/:containerId/reject
{
"reason": "Wrong influencer for this product"
}approveflipsapprovalStatustoapproved, stampsapprovedAtandapprovedBy(the API key id that approved), persists the optionalnote(max 1024 chars).rejectflipsapprovalStatustorejected, stampsrejectedAtandrejectedBy(API key id), persistsreason(required, 1–1024 chars). Rejected containers stay in the project for audit — they don't get deleted. To produce a new take, callPOST /v1/projects/:id/contentagain with a freshhook.
Actor attribution: both endpoints return approvedBy / rejectedBy from the calling API key. If you're building a review UI with multiple human reviewers, persist the reviewer's identity in your own system.
Rejection is a terminal decision for that container. Calling schedule or publish against a rejected container returns 409 CONTENT_REJECTED — the gate never opens for it, even if project policy later changes.
Once scope enforcement ships, these endpoints will require content:approve. Today, org-scoped keys can call them directly.
Reading and patching the policy
GET /v1/projects/:projectId/content-review-policy
→ 200
{
"projectId": "5e9c0b1e-...",
"policy": "review_first_n",
"firstN": 3,
"pendingCount": 1,
"updatedAt": "2026-04-25T22:55:10.996Z"
}Patch with PATCH to change the policy. Send the new policy (and firstN if applicable):
PATCH /v1/projects/:projectId/content-review-policy
{
"policy": "review_first_n",
"firstN": 5
}To switch off the gate entirely:
PATCH /v1/projects/:projectId/content-review-policy
{
"policy": "auto_approve"
}Sending firstN together with auto_approve or review_all returns 422 VALIDATION with a clear details.issues pointer at firstN.