API
Create a key under Settings (Creator and Business plans). Send it as Authorization: Bearer ek_….
List brands and accounts
GET https://echoly.2cstudio.app/api/brands
Add a post
POST https://echoly.2cstudio.app/api/posts
{
"brand": "b_…",
"kind": "daily-stats",
"text": "Yesterday: 120 new players 🍌",
"facts": { "newPlayers": 120 },
"publish_at": "2026-10-27T20:00",
"accounts": ["x", "bluesky"],
"images": [{ "url": "https://example.com/card.png" }]
}
publish_at: epoch milliseconds, ISO time with a zone, or local time in your workspace's zone. Empty means as soon as approved.accounts: platform names or account ids; default is every connected account of the brand. At least one has to match a connected account, otherwise the post is refused with400 no_accounts.images: up to 4, each{"url": "https://…"}or{"base64": "…"}.thread: instead oftext, a list of 2–10 posts, e.g.["1/3 Season 1 recap 👇", "27 players…", "What next?"]. X, Bluesky and Mastodon publish them as replies to each other; Telegram and Discord get one message. Each post counts toward your X limit. (Intext, a line with only---does the same.)submit:falsekeeps it a draft. Otherwise it goes to approval; with automatic approval on and all guards passed it is scheduled right away.
Response: {"id", "status", "reasons", "url"}. reasons says why a post waits for a person.
Limits: at most 20 new posts a minute and 100 a day per brand. Over a limit the API answers 429 rate_limited with a Retry-After header; nothing is created. With automatic approval, a post with the same text as one from the last 7 days waits for a person.
Read and cancel
GET /api/posts?status=pending,scheduled&brand=b_… GET /api/posts/<id> POST /api/posts/<id>/cancel