calmposts

REST-API und Webhooks

Skripte und KI-Agenten arbeiten in einem Arbeitsbereich mit den Berechtigungen ihres Schlüssels. Jeder Beitrag, den sie anlegen, bekommt die source „api“.

REST-API v1

Unter /api/v1, angemeldet mit einem API-Schlüssel.

Gehosteter MCP-Server

Unter /mcp (Streamable HTTP), mit API-Schlüssel oder OAuth-Token von claude.ai oder ChatGPT.

MCP-Doku

Lokales MCP-Programm

calmposts-mcp bietet dieselben Tools über stdio, auf Basis der REST-API.

MCP-Doku

Die maschinenlesbare Referenz ist https://calmposts.com/api/v1/openapi.json (OpenAPI 3.1, öffentlich).

API-Schlüssel

Der Inhaber des Arbeitsbereichs legt Schlüssel unter „API & MCP“ in der App an und wählt Namen und Berechtigungen. Der Schlüssel (cp_live_…) wird einmal angezeigt; calmposts speichert nur seinen SHA-256-Hash und die ersten 12 Zeichen.

Ein widerrufener Schlüssel wird bei der nächsten Anfrage abgewiesen. Ein Schlüssel hört auch auf zu funktionieren, wenn sein Ersteller gesperrt oder gelöscht wird oder den Arbeitsbereich verlässt. Wie viele Schlüssel möglich sind, legt der Tarif fest; der kostenlose hat keine.

Jede Anfrage sendet Authorization: Bearer cp_live_…. Jeder Schlüssel hat 60 Anfragen pro Minute, REST und MCP zusammen; darüber hinaus lautet die Antwort 429 rate_limited mit Retry-After in Sekunden.

BerechtigungErlaubt
accounts:readGET /accounts, GET /limits
posts:readGET /posts, GET /posts/:id, GET /queue/next
posts:writePOST /posts, PATCH /posts/:id, DELETE /posts/:id, POST /posts/:id/retry
media:writePOST /media

Fehler

Fehler haben ein gemeinsames Format: {"error": "…", "code": "…", "field": "…"}.

Ein Beitrag, der die Prüfung nicht besteht, ist ein 422: field und code nennen das erste Problem (targets[0].parts[0].text, too_long), das Array errors listet alle. Ein Kontingent ist 402 quota_exceeded mit der Metrik als field; eine fehlende Berechtigung 403 insufficient_scope.

Aufrufe

Vorbereitung

Legen Sie den Schlüssel in eine Umgebungsvariable; die Beispiele unten nutzen $auth.

export CALMPOSTS_API_KEY=cp_live_…
export CALMPOSTS=https://calmposts.com
auth=(-H "Authorization: Bearer $CALMPOSTS_API_KEY")

Konten

Jedes verbundene Konto mit den Regeln seines Netzwerks und seinem Zeichenlimit.

curl "${auth[@]}" $CALMPOSTS/api/v1/accounts
# {"items":[{"id":"…","provider":"x","handle":"@carol","status":"active","char_limit":280,"capabilities":{…}}]}

Limits

Der Verbrauch des Tarifs in diesem Zeitraum, die Zeitzone des Arbeitsbereichs und die Regeln jedes Netzwerks.

curl "${auth[@]}" $CALMPOSTS/api/v1/limits
# {"timezone":"Europe/Berlin","billing":{"plan":{…},"usage":{"posts_per_day":{"used":3,"limit":30,…}}},"networks":[…]}

Beitrag anlegen

schedule.mode ist draft, queue (der nächste freie Termin der Konten), at (mit einer Zeit nach RFC 3339) oder now. Mehrere Teile ergeben einen Thread; eigene parts eines Ziels geben diesem Kanal eigenen Text.

curl -X POST $CALMPOSTS/api/v1/posts "${auth[@]}" -H "Content-Type: application/json" -d '{
  "parts":    [{"text": "Calm posts, scheduled."}],
  "targets":  [{"account_id": "ACCOUNT_ID"}],
  "schedule": {"mode": "queue"}
}'
# 201 {"id":"…","status":"scheduled","scheduled_at":"…","version":1,"source":"api",…}

Bild anhängen

Erst hochladen, dann die Medien-ID in einem Teil übergeben.

curl -X POST $CALMPOSTS/api/v1/media "${auth[@]}" -F file=@cat.png -F alt="A calm cat"
# 201 {"id":"MEDIA_ID","kind":"image",…}  →  "parts": [{"text": "…", "media_ids": ["MEDIA_ID"]}]

Auflisten und lesen

Filter: status, account_id, from und to (RFC 3339), q, limit und offset.

curl "${auth[@]}" "$CALMPOSTS/api/v1/posts?status=scheduled,failed&limit=20"   # {"items":[…],"total":n}
curl "${auth[@]}" $CALMPOSTS/api/v1/posts/POST_ID

Bearbeiten

PATCH ändert, was der Body nennt, ausgehend von der gelesenen Version; eine veraltete Version ergibt 409 version_conflict. Bearbeitbar sind nur Entwürfe und geplante Beiträge.

curl -X PATCH $CALMPOSTS/api/v1/posts/POST_ID "${auth[@]}" -H "Content-Type: application/json" \
  -d '{"version": 1, "schedule": {"mode": "at", "at": "2026-10-21T09:30:00+02:00"}}'

Löschen, wiederholen, nächster Termin

DELETE storniert einen geplanten Beitrag (ein erschienener bleibt im Netzwerk). retry sendet die gescheiterten Kanäle sofort erneut. queue/next zeigt, wohin „queue“ einen Beitrag legen würde.

curl -X DELETE "${auth[@]}" $CALMPOSTS/api/v1/posts/POST_ID          # 204
curl -X POST "${auth[@]}" $CALMPOSTS/api/v1/posts/POST_ID/retry      # the post
curl "${auth[@]}" "$CALMPOSTS/api/v1/queue/next?account_ids=A,B"     # {"at":"2026-10-21T09:00:00Z"}

Webhooks

Statt zu pollen, lassen Sie calmposts Ihrem System sagen, was passiert ist. Jede Zustellung ist ein POST mit JSON-Body; die id ist eindeutig, eine Wiederholung lässt sich also verwerfen.

EreignisGesendet, wenn
post.publishedalle Kanäle eines Beitrags erschienen sind (data: post_id und die url jedes Ziels)
post.failedein Kanal eines Beitrags endgültig gescheitert ist (data: post_id, target_id, error_code, error)
post.pending_approvalder Beitrag eines Agenten auf die Freigabe durch einen Menschen wartet
account.needs_reconnectein Netzwerk den Zugriff widerrufen hat und das Konto neu verbunden werden muss
{
  "id": "…",
  "type": "post.published",
  "workspace_id": "…",
  "created_at": "2026-10-21T09:00:03Z",
  "data": {"post_id": "…", "targets": [{"id": "…", "account_id": "…", "provider": "x", "url": "https://x.com/…"}]}
}

Der Body enthält weder Token noch Geheimnisse.

Einen Agenten über MCP verbinden