staqlydocs
REST API

REST API overview

Learn how to call the staqly REST API from scripts, workflows and your own apps.

Edit on GitHub

The REST API is the plain-HTTP way into staqly. Use it from shell scripts, n8n, CI jobs or any language with an HTTP client. Everything else is built on top of it: the dashboard, the CLI, the SDKs and the MCP server all go through the same services and permission checks.

You areUse this
An AI agent in Claude Code, Codex, OpenCode or another MCP clientMCP, no HTTP code needed
A person or shell script in a terminalThe CLI (staqly posts create "Product updates" --content "Hello")
A TypeScript / JavaScript appThe TypeScript SDK, typed, with paging helpers
A Python appThe Python SDK, sync and async
Anything else: n8n, Go, CI, curlThis REST API

Make your first requests

Start staqly and check it's up

Run staqly serve (one binary, API and MCP on 127.0.0.1:3333) or the Docker image. Then:

curl -s http://localhost:3333/health
# → {"status":"ok","version":"v0.3.0"}

Every other request needs Authorization: Bearer ...: in Staqly Community Edition (staqly serve, the Docker image) the access token staqly prints at start, in cloud mode an API key stk_.... The examples in these docs read it from STAQLY_API_KEY:

export STAQLY_API_KEY=$(staqly serve token)   # Docker: $(docker exec staqly server token)

See Authentication.

Connect a channel

The catalog says what each platform needs; your own automation needs nothing:

API=http://localhost:3333

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" $API/v1/providers | jq '.data[] | select(.status=="available") | {id, auth, credential_fields}'
curl -s -H "Authorization: Bearer $STAQLY_API_KEY" -X POST $API/v1/channels -H 'Content-Type: application/json' \
  -d '{"provider":"custom","name":"My publisher"}'
# → {"id":"ch_7h2kq9xw3nbvd5yhzm6a","provider":"custom","status":"connected", ...}

Create a post

Post into a collection by name (or col_qjuybkx6355nz4no6pqk ID). The Idempotency-Key makes a retry safe:

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" -X POST $API/v1/posts \
  -H 'Content-Type: application/json' -H 'Idempotency-Key: gen-42' \
  -d '{"collection":"Default","content":"v1.4 is out.","targets":[{"channel_id":"ch_7h2kq9xw3nbvd5yhzm6a"}],"publish_at":"2026-11-03T09:00:00Z","status":"scheduled","source":"n8n"}'
# → {"id":"post_oalirbz67y4m4cqxq5dw","status":"scheduled","version":1, ...}

Leave out publish_at and status for a draft; send "publish_now": true to publish at once. Add "repeat_every": "1w" (or 1d to 6d, 2w, 1mo) to bring the post back after each publish; see Repeating posts. Leave out source and staqly names the client from its User-Agent instead (curl, or the agent name an SDK appends).

Follow what happens

At 09:00 a job publishes each target. Subscribe a webhook to post.published and post.failed, or read the post:

curl -s -H "Authorization: Bearer $STAQLY_API_KEY" $API/v1/posts/post_oalirbz67y4m4cqxq5dw | jq '{status, targets}'
# → {"status":"published","targets":[{"channel_id":"ch_...","status":"published","external_url":"https://..."}]}

A target on your own automation stays publishing until your script reports with POST /v1/posts/{id}/targets/{channel}/result; see Your own automation.

Conventions

The contract is an OpenAPI 3.2 document: apps/api/openapi.yaml in the repository, served live at http://localhost:3333/openapi.yaml, and rendered in the API reference.

ConventionValue
Base URLhttp://localhost:3333 (Community Edition) · https://<your host> (cloud)
VersioningEverything lives under /v1
FormatsJSON request and response bodies; multipart/form-data for uploads. Other content types are rejected. Unknown JSON fields are rejected (400).
AuthAuthorization: Bearer ...: the access token stk_local_... in the Community Edition, an API key stk_... in cloud mode (Authentication)
Errors{"error":{"code","message","fields","details"}} (Errors)
Lists{"data":[],"next_cursor":"eyJjIjoiMjAyNi0xMC0xMyJ9"} with opaque keyset cursors (Pagination & search)
RetriesIdempotency-Key header, 24 h replay window (Idempotency)
Async workPublishing and schedule runs answer 202 and happen in the job queue
TimestampsISO-8601 UTC strings; filters accept RFC 3339 or YYYY-MM-DD
IDsPrefixed: col_qjuybkx6355nz4no6pqk, post_oalirbz67y4m4cqxq5dw, ch_7h2kq9xw3nbvd5yhzm6a, sch_5k2nq8rx7bvm3dz4hcye, job_r8m3kz6qx2nb7vd5hcwt, evt_looiyqe5kfkjh4xedcnd, wh_lv5rc7rttjnfwnnsn4ny, dlv_bgx4s2p4xnzimajdkoki, nch_ce3g6qaygtlxifq4bdmv (alerts), exp_zxj2lzvmh32poxxq2kgp, med_t4w6pz2kqm7xr3nb5vya, mfd_t3nyil5dk3q253gejo5a (media folders), grp_eowhj2p7qxt6a2a7pmoq (channel groups), key_r7zq2m4xk3pw6ta5ned2, ws_h2c5vq7nzk4mtr6pxw3e
Request IDsEvery response has X-Request-Id; errors include request_id

Every response also carries X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, X-Frame-Options: DENY and Cache-Control: no-store. Request logs contain paths only, never headers or query strings.

Endpoints by area

AreaEndpoints
SetupGET /v1/setup, POST /v1/workspaces (cloud signup), GET /v1/plans
Collections/v1/collections, /v1/collections/{id} ({id} may also be the exact name), GET /v1/collections/{id}/posts
Posts/v1/posts, /v1/posts/{id}, /schedule, /publish, /cancel, /duplicate, /events, /preview, /metrics, /targets/{channel}/result, /targets/{channel}/retry, /targets/{channel}/metrics, /purge; POST /v1/posts/delete (several at once); GET /v1/tags; GET /v1/work (what your automation is asked for)
Preview links & feedback/v1/posts/{id}/previews, /v1/posts/{id}/previews/{preview}, /v1/posts/{id}/comments (resolve, reopen, delete); the public page uses /v1/previews/{token} (Share a preview)
ChannelsGET /v1/providers, PUT/DELETE /v1/providers/{id}/app, /v1/channels, /v1/channels/{id}, /v1/channels/{id}/test, GET /v1/channels/oauth/{provider}/start, /callback, /v1/channel-groups (Channels); /v1/channel-links and the public /v1/connect-links/{token} (Connect links)
Schedules/v1/schedules, /v1/schedules/{id}, POST /v1/schedules/{id}/run (Schedules)
JobsGET /v1/jobs, GET /v1/jobs/stats, GET /v1/jobs/{id}, POST /v1/jobs/{id}/retry, POST /v1/jobs/{id}/cancel (Jobs)
Media/v1/media, /v1/media/{id}, /info, /usage, /restore, /keep, /replace, POST /v1/media/delete, POST /v1/media/bulk, /v1/media-folders (Media uploads); /v1/media-projects, /v1/ai (Media editor)
Events/v1/events (filters type, type_prefix, post_id, channel_id, schedule_id, actor_type, actor_id, agent, from, to) (Event types)
Webhooks/v1/webhooks, /v1/webhooks/{id}/test, /v1/webhook-deliveries, /v1/webhook-deliveries/{id}/retry (Webhooks)
Alerts/v1/alerts, /v1/alerts/{id}, /test, /deliveries (Alerts)
AgentsGET /v1/agents, DELETE /v1/agents/{id} (Agents)
Export & privacy/v1/events/export, /v1/workspace/export, /v1/workspace/exports/{id}, /v1/workspace/exports/{id}/download, /v1/posts/{id}/purge, DELETE /v1/workspace (cloud, dashboard session only) (Data export)
Workspace & auth/v1/session, /v1/sessions, /v1/sessions/{id}, /v1/sessions/api-key, /v1/sessions/recover, /v1/workspace, /v1/workspace/usage, /v1/access-token, /v1/access-token/rotate (local), /v1/api-keys, /v1/api-keys/{id}/rotate (cloud), /v1/device-links, /v1/device-links/redeem, /v1/recovery-key/rotate
Accounts (cloud)/v1/auth/* (Google, email code, passkey sign-in), /v1/account, /v1/account/sign-in-methods, /v1/account/workspaces, /v1/session/workspace, /v1/members, /v1/invitations (Accounts)
Plan & billing (cloud)/v1/billing, /v1/billing/details, /v1/billing/checkout, /v1/billing/portal, /v1/billing/x-posts, /v1/billing/credits/checkout, /v1/billing/addons, /v1/billing/webhook (Stripe) (Plans, Plan & usage)
Health & metricsGET /health, /livez, /readyz, /metrics, /openapi.yaml (Monitoring)
MCPPOST /mcp, Streamable HTTP, see MCP

Request and response schemas for each endpoint are in the API reference.

Next steps

On this page