مرجع API و MCP

ابنِ على Postclay

كل ما يفعله لوحة التحكم يفعله الـ API. المرجع التقني أدناه محفوظ بالإنجليزية — الشيفرة تبقى شيفرة في كل لغة.

Authentication

Every request is authenticated with an API key in the standard Authorization header. A key belongs to exactly one workspace, and every call is scoped to that workspace.

Authorization: Bearer pm_live_<your-key>

Base URL: https://postclay.com/api. Missing key → 401 MISSING_API_KEY; unknown or revoked → 401 INVALID_API_KEY.

Create an API key

Create a key from the dashboard, or via the API. The secret is shown once — store it now.

POST https://postclay.com/api/api-keys
Authorization: Bearer <session>
Content-Type: application/json

{ "name": "My integration" }

# 201 Created — the raw key appears only in this response
{ "key": "pm_live_ab12cd34…" }

GET /api-keys lists keys (never the secret): each has id, name, prefix, lastUsedAt, revokedAt, createdAt. DELETE /api-keys/:id revokes a key and returns 204.

Create a post

Creates a post in DRAFT. targets is required — one entry per connected account you want to publish to. Passing scheduledAt here stores the intent but does not schedule; call schedule to enqueue.

POST https://postclay.com/api/posts

{
  "content": "Hello from the Postclay API",
  "kind": "SINGLE",                       // SINGLE | CAROUSEL | THREAD
  "mediaIds": ["media-uuid"],             // optional, ordered, ≤ 50
  "labels": ["launch"],                   // optional
  "targets": [
    { "socialAccountId": "account-uuid",
      "contentOverride": null,            // null = inherit "content"
      "mediaIds": null }                  // null = inherit; [] = no media
  ]
}

Response — the created post (truncated):

201 Created
{
  "id": "post-uuid",
  "status": "DRAFT",
  "kind": "SINGLE",
  "content": "Hello from the Postclay API",
  "mediaIds": ["media-uuid"],
  "targets": [
    { "id": "target-uuid", "socialAccountId": "account-uuid",
      "platform": "X", "status": "PENDING", "attempts": 0,
      "externalPostId": null, "permalink": null }
  ],
  "createdAt": "2026-08-20T09:00:00.000Z"
}

curl

curl https://postclay.com/api/posts \
  -H "Authorization: Bearer pm_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "Hello from the Postclay API",
    "kind": "SINGLE",
    "targets": [{ "socialAccountId": "account-uuid" }]
  }'

Schedule a post

Moves a draft to SCHEDULED and enqueues each target. Both fields are required. scheduledAt is absolute when it carries a Z or offset, otherwise it is wall-clock time in timezone.

POST https://postclay.com/api/posts/:id/schedule

{ "scheduledAt": "2026-08-20T18:30:00Z", "timezone": "Asia/Riyadh" }

# 200 OK — the full post, status now "SCHEDULED"

List posts

Returns a paginated envelope. Filter by status, label, or date range.

GET https://postclay.com/api/posts?status=scheduled&page=1&pageSize=20

# status: scheduled | published | draft | failed
# label:  string   from,to: ISO-8601   pageSize: 1–100 (default 20)

{ "data": [ /* posts */ ], "total": 42, "page": 1, "pageSize": 20 }

Delete a post

Cancels any pending targets and deletes the post.

DELETE https://postclay.com/api/posts/:id

# 204 No Content

Idempotency

Postclay does not accept a client Idempotency-Key header today — treat POST /posts as create-once and store the returned id. Retries are safe downstream: each target carries an internal, server-generated idempotency value so the publish workers never double-post to a platform on retry. (A client-facing idempotency key is on the roadmap; this note will change when it ships.)

Webhooks

Subscribe a URL to publish events. Two events fire: POST_PUBLISHED and POST_FAILED.

POST https://postclay.com/api/webhooks
{ "url": "https://you.example/hook",
  "events": ["POST_PUBLISHED", "POST_FAILED"] }

# 201 — response includes "secret" once (used to verify signatures)

Delivered payloads:

// POST_PUBLISHED
{ "event": "POST_PUBLISHED", "occurredAt": "2026-08-20T12:00:00.000Z",
  "data": { "postId": "…", "targetId": "…", "platform": "X",
            "externalId": "…", "permalink": "https://…" } }

// POST_FAILED
{ "event": "POST_FAILED", "occurredAt": "2026-08-20T12:00:00.000Z",
  "data": { "postId": "…", "targetId": "…", "platform": "X",
            "error": "…" } }

Every delivery is signed. Verify the HMAC before trusting a payload:

Content-Type: application/json
X-Postclay-Signature: sha256=<hex hmac of the raw body, keyed by your secret>
X-Postclay-Event: POST_PUBLISHED
X-Postclay-Delivery: <delivery-uuid>

# verify (Node):
const mac = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const ok  = "sha256=" + mac === req.headers["x-postclay-signature"];

Deliveries retry up to 5 times with exponential backoff (10s per-request timeout); internal/loopback URLs are refused. POST /webhooks/:id/test sends a test delivery (202); POST /webhooks/:id/rotate-secret issues a new secret.

Rate limits & quotas

There are no per-plan API rate limits. Two real limits apply:

Per-IP limits guard unauthenticated and AI endpoints (e.g. signup, login, caption/hashtag generation). The authenticated /posts, /media, /webhooks, /api-keys and /mcp endpoints are not IP-limited. On exhaustion you get:

429 { "error": { "code": "RATE_LIMITED", "messageKey": "errors.rateLimited" } }

Per-plan quotas cap connected accounts and team seats (posts are unlimited on every tier):

PlanConnected accountsSeatsScheduled posts
Trial2510Unlimited
Builder83Unlimited
Team2510Unlimited
ScaleUnlimitedUnlimitedUnlimited

A “connected account” is one social identity linked to your workspace (a specific X handle, Instagram business account, Facebook page, and so on). Reconnecting the same identity reuses its slot.

MCP — connect your agent

Postclay is an MCP server, so any MCP client can create, schedule, list and cancel posts as a first-class user. The endpoint is stateless Streamable-HTTP and takes the same API key:

POST https://postclay.com/api/mcp
Authorization: Bearer pm_live_<your-key>

Claude Code

Add Postclay to your MCP config:

{
  "mcpServers": {
    "postclay": {
      "url": "https://postclay.com/api/mcp",
      "headers": { "Authorization": "Bearer pm_live_<your-key>" }
    }
  }
}

Cursor

Add the same server block to Cursor’s MCP settings (~/.cursor/mcp.json or Settings → MCP). The url and Authorization header are identical to the snippet above.

Tools

The server exposes eight tools:

list_platforms()        list_accounts()
create_post({ content, kind: "SINGLE"|"CAROUSEL",
              targets: [{ socialAccountId }], mediaIds?, timezone?, recurrence? })
schedule_post({ postId, scheduledAt, timezone })
list_posts({ status?, from?, to?, page?, pageSize? })
get_post({ postId })     cancel_post({ postId })
upload_media({ filename, mimeType, dataBase64 })

A ready-made skill file is published at postclay.com/skill.md — point OpenClaw / Hermes-style agents at it and they pick up these tools natively.

Questions? [email protected] · Back to home