# Preview or atomically edit scheduled posts > Markdown version of https://docs.postlake.dev/api/patch-posts-bulk . The canonical page for humans. > PostLake is the social media API for AI agents: https://postlake.dev/llms.txt `PATCH /v1/posts/bulk` Select 1 to 200 exact post IDs. First send dryRun=true and inspect the per-post before/after diff. Apply with dryRun=false and the returned snapshot. A changed, missing, non-scheduled or imminent post rejects the entire edit. A UTC shift updates all scheduled times atomically in storage; timer re-arming is best-effort, with the due-post cron as a backstop. Cancellation is a separate endpoint. ## Request Authenticate with a bearer API key. Agents calling over MCP authorise with OAuth instead and never handle the key. ```bash curl -X PATCH https://api.postlake.dev/v1/posts/bulk \ -H "Authorization: Bearer $POSTLAKE_API_KEY" \ -H "Content-Type: application/json" \ -d '{"ids":["post_abc","post_def"],"patch":{"text":"Revised campaign caption"},"dryRun":true}' ``` ### Body | Field | Type | Required | What it is | | --- | --- | --- | --- | | `ids` | array of string | Yes | | | `patch` | object | Yes | | | `patch.text` | string | No | | | `patch.textOverrides` | object | No | | | `patch.accounts` | array of string | No | Exact final destination list for every selected post. | | `patch.addAccounts` | array of string | No | Add these connected account IDs to each post, preserving its existing destinations and avoiding duplicates. Mutually exclusive with accounts. Each final destination is validated. Preview and apply must use the same patch. | | `patch.shiftMinutes` | integer | No | Shift each scheduled UTC instant by this many minutes; all resulting times must remain in the future. | | `patch.campaign` | string | No | | | `patch.assetMetadata` | object | No | | | `dryRun` | boolean | Yes | | | `snapshot` | string | No | Required for apply; copy from the preview response. | ## Response Preview or full edit, with per-post before/after results and snapshot. (`200`). | Field | Type | Required | What it is | | --- | --- | --- | --- | | `dryRun` | boolean | Yes | | | `snapshot` | string | Yes | | | `matched` | integer | Yes | | | `results` | array of object | Yes | | ### Example ```json { "dryRun": false, "snapshot": "…", "matched": 0, "results": [ { "id": "…", "before": "…", "after": "…" } ] } ``` ## Calling this from an agent Agents do not call REST. They call tools. Over the hosted MCP server this operation is `list_posts`, and the agent authorises with OAuth so your API key is never pasted into a chat or seen by the model. You control what any one agent may do: which brands it may act for, which networks it may reach, how much a day, and whether it may spend credits bought up front. ## Errors Every failure uses one shape: a stable `type`, a granular `code`, the field at fault in `param`, and a plain-language `fix`. See https://docs.postlake.dev/errors.md . Check the result before retrying a write. Only operations that explicitly support idempotency can safely replay an `Idempotency-Key`. ## Common questions **Can an AI agent do this without the REST API?** Yes. Over MCP the same job is the list_posts tool. The agent authorises with OAuth, so no API key is pasted into a chat or seen by the model, and the answer comes back in the same normalised shape. **Is it safe to retry this call?** Do not automatically repeat a write after a timeout unless that endpoint explicitly documents idempotency. First read the resulting state; a second request could repeat the action. **What happens when it fails?** You get one error shape: a stable type, a granular code you can branch on, the offending field in param, and a fix written so an agent can correct itself rather than stop. Full list on Errors and retries. ## Related - [Cancel a scheduled post, discard a draft, or retract a live one](https://docs.postlake.dev/api/delete-posts-by-id.md) `DELETE /v1/posts/{id}` - [Recover a creation receipt without replaying a write](https://docs.postlake.dev/api/get-idempotency-receipt.md) `GET /v1/idempotency/{key}` - [List posts](https://docs.postlake.dev/api/get-posts.md) `GET /v1/posts` - [Get post](https://docs.postlake.dev/api/get-posts-by-id.md) `GET /v1/posts/{id}`