---
title: "Queue a post for a later time"
method: POST
path: "/api/schedule"
---

# Queue a post for a later time

`POST /api/schedule`

## Request

- Base URL: `https://postwire.io`
- URL: `https://postwire.io/api/schedule`
- Auth: HTTP bearer

Rules are checked NOW, not at send time: a disconnected network or missing media is rejected while you build the flow rather than silently failing overnight. The Free plan keeps 3 scheduled posts waiting at a time: past that the post is not refused but saved as held (202, code queue_limit, with an upgrade link) and scheduled automatically when the plan is upgraded; it is never published on Free and can be canceled. On Free only the first 2 networks of a post are published when it runs; the answer names the others in held_networks. run_at "next_slot" puts the post in the brand's next free queue slot (GET/PUT /api/brands/{id}/slots); the Free queue cap applies the same way.

## Request body

- ScheduleRequest
  - `platforms` string[], required
  - `text` string
  - `run_at` string, required: When to publish: an ISO 8601 time in the future (up to 365 days), or "next_slot" for the brand's next free queue slot. A post goes to the earliest slot that every one of its networks has and that none of them already uses for another waiting (queued, held or publishing) post of the same brand. When its networks share no slot, it goes to the earliest slot of any of them that is free for all. Slots less than 2 minutes away are skipped; the search looks 28 days ahead. The answer then carries slot { run_at, timezone, local, shared }.
  - `video_url` string, uri
  - `brand_id` string, uuid
  - `label` string
  - `timezone` string: With run_at next_slot: the IANA timezone to read the slots in when the brand has none saved (e.g. America/Lima). Default UTC. Not stored with the post.

## Response `200`

{ ok, scheduled: {...}, targets[], message, held_networks?, held_message?, upgrade? }

## Other responses

- `202`: Saved as held, not scheduled: the plan's queue is full. { ok: true, held: true, code: "queue_limit", scheduled: { status: "held", ... }, scheduled_queue: { used, limit, held }, message, upgrade, upgrade_url, upgrade_message } Or: 202 = held for approval (the brand's approval rules or a limit on this key): nothing was published. { ok: true, posted: 0, status: "pending_approval", id (the post's PostWire id, for GET /api/post/status), approval_id, approval: { reasons, notified: [{ to (masked), role, via, sent }], review_url (the dashboard; sign-in needed), who_can_approve, publishes }, results: [{ platform, ok: false, status: "pending_approval", code: "pending_approval", account }], message_for_user }. Only a person approves it: the emailed link or a signed-in dashboard session, never an API key or an AI connector. A scheduled post approved before its time goes out at that time; approved after it, right after approval (or at a new time the approver picks).
- `400`: run_at in the past, network not connected, media required, or the 500-post sanity ceiling
- `409`: code no_free_slot (run_at next_slot found no free slot in 28 days), or ambiguous_brand

---

[API](https://skmtc.dev/postwire/apis/postwire-api.md) · [All operations](https://skmtc.dev/postwire/apis/postwire-api/llms.txt) · [OpenAPI document](https://skmtc.dev/postwire/apis/postwire-api/revisions/513a409ab8f7?raw)
