Human approval

Agents draft.
You approve.

A prompt that says 'always create a draft' is guidance, not a security boundary. PostLake lets the owner enforce approval on each OAuth agent. Once enabled, publish requests enter awaiting_approval at the API boundary and the same agent cannot approve them. Existing agents keep direct publishing until the owner turns the policy on.

Get started free →

Free tier. No card · one API for every network · full MCP access

In shortTurn on Require publishing approval for an agent. PostLake forces its publish requests into the approval queue and prevents that agent from approving its own work. API-key workflows can use draft: true for an ordinary personal draft.

When this guide is for you

You want an agent to draft social posts, and a person to approve them, without a second approval product.

Before you start

Same setup the docs quickstart uses. Do this once:

  1. Sign up at app.postlake.dev and verify your email (unlocks free credits).
  2. On Channels, create a profile (e.g. my-brand) and connect at least one account. Bluesky is the fastest first channel: no app review. Instagram/TikTok/Facebook need each platform's review before API posting.
  3. Account menu → API Keys → create a key (sk_live_…). It shows once; treat it like a password.

Create a draft, then publish it

The owner sets the rule once. The agent writes, PostLake saves a draft, and only the owner or an account-level workflow can send it.

# 1. Agent writes. Nothing goes live. Nothing is charged.
curl -X POST https://api.postlake.dev/v1/posts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: recap-draft-001" \
  -d '{
    "text": "Friday recap from the changelog",
    "profile": "my-brand",
    "scheduledAt": "2026-09-04T09:00:00",
    "timezone": "Europe/London",
    "draft": true
  }'

# A guarded agent receives state "awaiting_approval" automatically.

# 2. After you read it: publish. Keeps the scheduled time unless you override.
curl -X POST https://api.postlake.dev/v1/posts/post_a1b2c3/publish \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

# MCP OAuth: turn on Require publishing approval under Agents. create_post
# then saves an approval request automatically, and the owner decides in PostLake.

Make it work

Tool-specific glue on top of the shared setup above:

  1. Open Agents, edit the connection's guardrails, and turn on Require publishing approval.
  2. The agent writes with the same fields as a live post (text, media, overrides, scheduledAt).
  3. List requests with GET /v1/posts?state=awaiting_approval or open Posts → Awaiting approval.
  4. Review the post, then choose Approve and publish, Approve and schedule, Edit, or Reject. An approval request can change destinations before the owner decides.
  5. The guarded agent can read its state but cannot call publish_draft or POST /v1/posts/{id}/publish to approve itself. A failed approval attempt moves into normal failed-post handling rather than back into the queue.

Read the response (don't skip this)

You get one Post with an overall state and a targets[] array. One entry per account. Always check each target; partial success is normal.

{
  "id": "post_a1b2c3",
  "state": "partial",
  "targets": [
    { "platform": "bluesky",  "state": "published", "url": "https://bsky.app/…" },
    { "platform": "linkedin", "state": "failed",
      "error": { "type": "invalid_request", "message": "…", "retryable": false } }
  ]
}

Where the post goes

Same rules as the docs. Pick one addressing style:

See Publishing: where to post.

Do more (same API)

Pitfalls specific to this path

Want zero wrapper code? Connect the hosted MCP server (https://api.postlake.dev/mcp) over OAuth. Same accounts and responses as this API path. Agents overview.

Common questions

How do I stop an AI agent from publishing until I approve?

Turn on Require my approval for the agent under Agents. PostLake then saves every publish request from that OAuth connection as a draft, even if the agent omits draft: true, and prevents it from self-approving. Publish from PostLake after review.

Is this like pendpost or Rolino?

Same job (fail-closed approval), different shape. Those are local-first apps. PostLake is a hosted API: the draft lives next to scheduling, analytics and MCP, so the agent does not need a second vendor for the gate.

Can a draft also be scheduled?

Yes. Save it with scheduledAt. publish_draft keeps that time unless you send a new one, or clear it to publish immediately.

Go deeper in the docs

These guides stay short on purpose. Canonical behaviour lives here:

Also: Media · Errors · MCP · Analytics

Related guides

All guides · Full docs · llms.txt · Markdown

Stuck? The docs are the source of truth, start at Publishing.

Open the dashboard →