Reference
API Reference
Once Open-Dispatch is running, the API is available at http://localhost:8000. No auth is required by default — it's designed for trusted self-hosting. Front it with Cloudflare Access, Tailscale, or basic auth if you expose it to the internet.
Confirm it's running
Run this first. You should see {"status":"ok"}.
curl http://localhost:8000/healthz
/healthzReturns JSON for API clients, the HTML dashboard for browsers.
Request
curl http://localhost:8000/healthz
Response
{"status": "ok"}/dispatchEnqueues a ContentUnit. Returns a queue row ID you can poll.
Request
curl -X POST http://localhost:8000/dispatch \
-H "Content-Type: application/json" \
-d '{
"targets": ["twitter", "bluesky"],
"formats": {
"twitter_thread": {
"tweets": ["your tweet here"]
},
"bluesky_post": {
"text": "your post here"
}
}
}'Response
{"id": "abc123", "status": "queued", "targets": ["twitter", "bluesky"]}/queueFilter by status: queued, publishing, published, failed, dead.
Request
curl "http://localhost:8000/queue?status=published"
Response
[{"id": "abc123", "status": "published", "platform": "twitter", ...}]/queue/{id}Content-negotiated — JSON for API clients (Accept: application/json), HTML detail page for browsers.
Request
curl -H "Accept: application/json" http://localhost:8000/queue/abc123
Response
{"id": "abc123", "status": "published", "post_id": "1234567890", ...}/queue/{id}/retryResets the row to queued so the worker picks it up again.
Request
curl -X POST http://localhost:8000/queue/abc123/retry
Response
{"id": "abc123", "status": "queued"}/queue/{id}Permanently removes the row. Cannot be undone.
Request
curl -X DELETE http://localhost:8000/queue/abc123
Response
{"deleted": true}/ai/adaptTakes a source caption and returns platform-optimised versions. Uses Ollama → OpenRouter → heuristic fallback.
Request
curl -X POST http://localhost:8000/ai/adapt \
-H "Content-Type: application/json" \
-d '{
"text": "We just shipped v0.4 — self-host free, MIT.",
"platforms": ["twitter", "linkedin", "instagram"]
}'Response
{"twitter": "v0.4 is live. self-host free, MIT ↓", "linkedin": "...", "instagram": "..."}/media/transcodeSends back the resized image as binary. Accepts image/* body.
Request
curl -X POST "http://localhost:8000/media/transcode?platform=twitter" \ -H "Content-Type: image/jpeg" \ --data-binary @photo.jpg \ --output photo.twitter.jpg
Response
(binary JPEG)
/media/specsReturns dimensions, aspect ratio, and format for all 10 built-in specs.
Request
curl http://localhost:8000/media/specs
Response
{"twitter": {"width": 1600, "height": 900, "format": "JPEG"}, ...}ContentUnit — full shape
{
"category": "general", // optional label
"targets": ["twitter:work", "bluesky", "threads"],
"scheduled_for": "2026-06-26T18:00:00+00:00", // omit to post immediately
"formats": {
"twitter_thread": { "tweets": ["t1", "t2"], "media_paths": [] },
"bluesky_post": { "text": "…", "images": [{"path": "…", "alt": "…"}] },
"telegram_message": { "text": "…", "photo_path": "…", "parse_mode": "HTML" },
"instagram_post": { "caption": "…", "image_url": "https://…" },
"linkedin_post": { "text": "…", "asset_urn": "urn:li:digitalmediaAsset:…" },
"threads_post": { "text": "…", "image_url": "https://…", "video_url": "https://…" }
},
"webhook_url": "https://example.com/callback" // optional, fires on publish/fail
}Target syntax: platform[:account]. Per-account env vars are <PLATFORM>_<FIELD>_<ACCOUNT> (uppercase). twitter:work resolves to TWITTER_ACCESS_TOKEN_WORK.
Something missing or broken?
Open an issue