What hosted schedulers take from you
The whole path, end to end
A request enters the queue, a worker picks it up, an adapter publishes it, and a webhook reports the result. There is no hidden step between those four.
Adding a platform is one function
The worker owns retry, backoff, and webhook delivery, so an adapter only has to publish and report a result. Register it in one dict and document its format key. That is the whole integration surface.
def publish(unit: ContentUnit, account: str | None) -> tuple[bool, str, str]:
"""Return (ok, post_id, error_message)."""
# the worker handles retry, backoff, webhooksWhat it does, with the artifact that proves it
One request, no dashboard
Every action is a plain HTTP call. Drive it from cron, n8n, a CI job, or an AI agent. The browser UI is optional, never required.
POST /dispatch
Three queue backends
JSONL on disk needs no infrastructure. Redis or Postgres when you outgrow one worker. Selected by an environment variable, not a rewrite.
JSONL | Redis | Postgres
Ten adapters, one contract
Every platform implements the same function signature. The worker owns retry, backoff, and webhooks so an adapter never has to.
tuple[bool, str, str]
Per-platform media specs
Ten image specs and seven video specs are built in. Send an image, get it back at the exact dimensions the target expects.
GET /media/specs
Every endpoint, no client library required
Plain HTTP with JSON in and JSON out. Full request and response shapes live on the API reference page.
| Method | Path | Purpose |
|---|---|---|
| GET | /healthz | Liveness probe. JSON for clients, dashboard for browsers. |
| POST | /dispatch | Enqueue a ContentUnit for one or many platforms. |
| GET | /queue | List rows. Filter by queued, publishing, published, failed, dead, canceled. |
| GET | /campaign/{unit_id} | Every platform row for one dispatch. |
| POST | /campaign/{unit_id}/cancel | Cancel a campaign's queued rows. |
| GET | /queue/{id} | One row, content-negotiated as JSON or HTML. |
| POST | /queue/{id}/retry | Re-queue an errored or dead row. |
| DELETE | /queue/{id} | Delete a queue row permanently. |
| POST | /ai/adapt | Rewrite a caption per platform. |
| POST | /media/transcode | Resize an image to a platform spec. |
| GET | /media/specs | List the built-in image and video specs. |
Five ways in, shortest first
Open-Dispatch 0.4.0
macOS 13 Ventura or later · Apple Silicon and Intel
Download .dmgall releases · build from source: bash scripts/make-dmg.sh
SwiftUI menubar app that bundles the Python server, so there is no separate Python install. First launch: right-click, then Open, to bypass Gatekeeper once.
Questions worth answering before you install
- Which install method should I use?
- On macOS, the app is the shortest path: one download, drag to Applications, done. If you want a terminal workflow, Homebrew gives you a login service via brew services. On a Linux VPS, Docker avoids the Python setup entirely.
- How do I self-host on a VPS?
- Docker is the cleanest option. It runs as a non-root user, needs no Python on the host, and ships an optional Redis sidecar. The Docker section of the README walks through it step by step.
- Is it really free?
- Yes. It is MIT licensed with no cloud component, no telemetry, and no account to create. Your platform credentials stay in a .env file on your own machine.
- How do I reach the API?
- It listens on localhost:8000 with no auth by default, which suits trusted self-hosting. If you expose the port beyond localhost, set OPEN_DISPATCH_API_TOKEN first. That requires an Authorization header on every route except /healthz.
- Where do I get platform credentials?
- Each platform's developer console issues its own keys, and .env.example in the repo lists every variable Open-Dispatch reads, grouped by platform. Copy it to .env and fill in only the platforms you dispatch to. The API skips platforms you leave unset.
- Can I add another platform?
- Yes. The adapter contract is a single Python function, roughly 80 lines. Register it in adapters/__init__.py, document its format key in the README, and the worker handles retry, backoff, and webhooks for you.