Differentiators nobody owns

Adding an approval step to automated social posting (PostGate has no built-in reviewer role — here’s the pattern that works instead)

PostGate does not ship a multi-user approval workflow. Here is the real, honest DIY pattern teams use instead: draft: true, a review list from GET /api/posts?status=draft, and a human-triggered publish.

Quick answer

PostGate has no built-in approval workflow — no reviewer role, approve/reject state, or audit trail. The working pattern: create posts with draft: true, build a review list from GET /api/posts?status=draft, and have a human trigger POST /api/posts/{id}/publish to approve or DELETE to reject. The MCP server's create_post tool already defaults to drafts.

Let's be upfront about what this post is and isn't: PostGate does not have a built-in multi-user content-approval workflow. There is no reviewer role, no approve/reject state, and no audit trail of who signed off on what. The API models exactly three post states that matter here — draft, scheduled, and everything after — and nothing richer than that.

That's a real gap if you're picturing a Slack-approvals-style tool with named reviewers and a decision log. But it doesn't mean you can't put a human checkpoint between "an automation generated this post" and "this went out on the customer's real Instagram account." The pattern below — draft: true, a review list your own code builds, and a human-triggered publish — is what teams actually build on top of PostGate today, and it's worth being precise about, rather than either overclaiming a feature that isn't there or dismissing the workaround as not good enough to matter.

PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.

01

What post states does PostGate actually model?

Creating a post takes one of four shapes: pass draft: true and the post is saved without entering the delivery pipeline at all; pass an ISO 8601 scheduled_at and it's queued for that instant; pass scheduled_at: "queue" and it takes the next open slot on a recurring schedule; pass none of those and delivery starts immediately. There is no fifth option for "pending review by a named person." draft is the only waiting state that doesn't already commit to a publish time — which is exactly why it's the right primitive to build an approval step on top of, even though PostGate itself has no idea a human is supposed to look at it.

Create a draft for reviewPostGate API
curl -X POST "https://postgate.studio/api/posts?profile_group_id=grp_acme123" \
  -H "Authorization: Bearer $POSTGATE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: campaign-post-report-014" \
  -d '{
    "post": { "body": "Generated launch copy, pending review." },
    "profiles": ["pro_instagram123", "pro_linkedin456"],
    "media": ["https://cdn.example.com/launch.jpg"],
    "draft": true
  }'
02

Build the review list yourself with GET /api/posts?status=draft

PostGate has no reviewer-facing screen. What it does have is a filterable list endpoint: GET /api/posts accepts a status query parameter, so GET /api/posts?status=draft returns exactly the posts sitting in the waiting state your automation put them in — nothing published, nothing already scheduled. That's the entire data layer a review UI needs: page through it, render each draft's body, media, and target profiles, and let a human decide.

List posts awaiting reviewPostGate API
curl "https://postgate.studio/api/posts?status=draft&per_page=50" \
  -H "Authorization: Bearer $POSTGATE_API_KEY"
03

The review surface itself is entirely yours to build

This is the part PostGate genuinely doesn't help with, and pretending otherwise would be dishonest: there is no built-in reviewer role, no per-user permissions on individual posts, and no record of who approved what. What teams actually build is small and specific to their own workflow — a few common shapes:

  • An internal admin page that lists drafts from GET /api/posts?status=draft and has an "Approve" button wired to the publish call below.
  • A Slack notification posted when a draft is created (via a queued job or a poll of the draft list), with an approve/reject action that calls back into your own service.
  • A row in your own database that links a PostGate post_id to your application's own reviewer, status, and timestamp fields — the audit trail PostGate doesn't keep, kept where you actually need it: next to the rest of your customer data.

None of this needs to be elaborate. The only hard requirement is that whatever UI or notification you build reads from status=draft and writes back to PostGate only when a human has actually acted.

04

What actually flips a post out of draft state?

Two real, documented ways to move a post forward once it's approved:

  • POST /api/posts/{id}/publish — the direct "approved, send it now" action. It moves the post out of draft and into the delivery pipeline immediately; only a post currently in draft can be published this way.
  • PATCH /api/posts/{id} — updates a draft's content while it's still awaiting review: edit the body, media options, or a target scheduled_at value before anyone approves it. This is how a reviewer fixes a typo or swaps an image without recreating the whole post.

One precise detail worth getting right: PATCH can store a scheduled_at value on a draft, but that alone doesn't move it out of the draft state or into the delivery pipeline — only /publish does that, and it always publishes right away. If your approval step needs to approve now but deliver at a specific future time, don't rely on PATCH-ing a future scheduled_at onto a draft and expecting it to fire on its own. Instead, have your own scheduler or cron job call /publish at the moment you actually want it to go out — the approval decision and the delivery moment are two separate actions in your own system, even though PostGate only exposes one API call for the second one.

Approve and publish a reviewed draftPostGate API
curl -X POST "https://postgate.studio/api/posts/post_01JXYZ/publish" \
  -H "Authorization: Bearer $POSTGATE_API_KEY"
05

How do you reject a draft?

A rejected draft is simplest handled as DELETE /api/posts/{id}, which cancels a draft or scheduled post outright — see the Posts reference. If you want to keep a record of why something was rejected for your own reporting, store that in your own database alongside the post_id before deleting; PostGate itself won't remember the reason.

06

What if an AI agent is what generates the draft?

Worth calling out separately: if the thing proposing posts is an AI agent rather than a scheduled job or a marketer, PostGate's MCP server already defaults to this exact pattern without any extra code. The create_post tool creates a draft unless the call explicitly passes confirm: true — see SDKs & MCP. Give an agent a group-bound key with the narrowest scope it needs, and its output naturally lands in the same status=draft queue a human reviews before anything reaches a real account.

Frequently asked questions

Does PostGate have a built-in content approval workflow?

No. PostGate has no reviewer role, no approve/reject state, and no audit trail of who signed off on what. The API models draft, scheduled, and terminal states — nothing richer. Draft is the only waiting state that doesn't commit to a publish time, which makes it the right primitive to build your own approval step on, with the review surface living in your own application.

How do I publish a draft after it has been approved?

Call POST /api/posts/{id}/publish — it moves the post out of draft and into the delivery pipeline immediately, and only a post currently in draft can be published this way. Note that PATCH-ing a future scheduled_at onto a draft does not make it fire on its own: if you need to approve now but deliver later, have your own scheduler call /publish at the moment you want the post to go out.

How do I reject a draft post in PostGate?

The simplest handling is DELETE /api/posts/{id}, which cancels a draft or scheduled post outright. If you want a record of why something was rejected, store the reason in your own database alongside the post_id before deleting — PostGate itself won't remember it.

Do AI-generated posts need extra approval code?

Not with PostGate's MCP server — its create_post tool creates a draft unless the call explicitly passes confirm: true, so agent output naturally lands in the same status=draft queue a human reviews. Give the agent a group-bound API key with the narrowest scope it needs, and nothing reaches a real account without a human acting.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs