Install
openclaw skills install @miprinia/socialcannonPublish, schedule, and manage social media posts across Twitter/X, Facebook, Instagram, LinkedIn, TikTok, and YouTube. Content calendar with gap analysis, A/B testing, engagement inbox, AI content repurposing, optimal timing suggestions, auto-scheduling, and UTM tracking.
openclaw skills install @miprinia/socialcannonSocial media publishing API. Publish to Twitter/X, Facebook, Instagram, LinkedIn, TikTok, and YouTube from one API with scheduling, analytics, A/B testing, and AI-powered features.
Base URL: https://socialcannon.app
Before making API calls, you need credentials and at least one connected social account.
Sign up at socialcannon.app (Google sign-in, free tier included — no card required). Your Client ID and Client Secret are on the dashboard Settings page — these are the values for SOCIALCANNON_CLIENT_ID and SOCIALCANNON_CLIENT_SECRET.
Social accounts are connected via OAuth in the browser. Open the connect URL for each platform you want to use — you'll authorize SocialCannon and get redirected back:
| Platform | Connect URL |
|---|---|
| Twitter/X | https://socialcannon.app/api/connect/twitter?client_id=YOUR_CLIENT_ID |
https://socialcannon.app/api/connect/facebook?client_id=YOUR_CLIENT_ID | |
https://socialcannon.app/api/connect/instagram?client_id=YOUR_CLIENT_ID | |
https://socialcannon.app/api/connect/linkedin?client_id=YOUR_CLIENT_ID | |
| TikTok | https://socialcannon.app/api/connect/tiktok?client_id=YOUR_CLIENT_ID |
| YouTube | https://socialcannon.app/api/connect/youtube?client_id=YOUR_CLIENT_ID |
You can also connect accounts from the dashboard at Settings → Accounts. Instagram uses Facebook's OAuth flow — make sure you select the Facebook Page linked to your Instagram Business account.
Once you have credentials and at least one connected account, authenticate and create your first post:
# Get a token
TOKEN=$(curl -s -X POST https://socialcannon.app/api/v1/auth/token \
-H "Content-Type: application/json" \
-d "{\"grant_type\": \"client_credentials\", \"client_id\": \"$SOCIALCANNON_CLIENT_ID\", \"client_secret\": \"$SOCIALCANNON_CLIENT_SECRET\"}" \
| jq -r '.data.access_token')
# List your connected accounts
curl -s https://socialcannon.app/api/v1/accounts \
-H "Authorization: Bearer $TOKEN" | jq '.data'
# Publish a post (replace <account_id> with an ID from the list above)
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"accountId": "<account_id>", "content": "Hello from SocialCannon!"}'
Instead of raw HTTP, you can expose SocialCannon's 23 tools to any MCP-compatible agent with the @socialcannon/mcp package. Use the same SOCIALCANNON_CLIENT_ID / SOCIALCANNON_CLIENT_SECRET from your dashboard.
Hermes Agent — add to ~/.hermes/config.yaml:
mcp_servers:
socialcannon:
command: "npx"
args: ["-y", "@socialcannon/mcp"]
env:
SOCIALCANNON_CLIENT_ID: "your-client-id"
SOCIALCANNON_CLIENT_SECRET: "your-client-secret"
Claude Desktop — add an entry under mcpServers in claude_desktop_config.json with the same command/args/env.
The REST API documented below remains fully available; MCP is an optional convenience layer over the same endpoints.
All requests require a JWT Bearer token. Get one by exchanging your client credentials:
curl -X POST https://socialcannon.app/api/v1/auth/token \
-H "Content-Type: application/json" \
-d "{
\"grant_type\": \"client_credentials\",
\"client_id\": \"$SOCIALCANNON_CLIENT_ID\",
\"client_secret\": \"$SOCIALCANNON_CLIENT_SECRET\"
}"
Response:
{
"success": true,
"data": {
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "posts:read posts:write ..."
}
}
Use response.data.access_token as a Bearer token in all subsequent requests. Tokens expire after 1 hour — request a new one when you get a 401.
All requests below require this header:
Authorization: Bearer <access_token>
Content-Type: application/json
IMPORTANT: ALL responses are wrapped in a standard envelope. This includes the token endpoint.
{ "success": true, "data": { ... } }{ "success": false, "error": "message", "code": "ERROR_CODE" }When extracting data from any response, always read from response.data, not from the response root. For example, to get the access token: response.data.access_token, not response.access_token.
Accounts represent social media profiles connected via OAuth (see Getting Started above). You need at least one connected account before you can create posts.
curl https://socialcannon.app/api/v1/accounts \
-H "Authorization: Bearer $TOKEN"
Returns all connected social accounts with their platform, username, and status. Use the account id field when creating posts. Filter by platform with ?platform=twitter.
curl https://socialcannon.app/api/v1/accounts/<account_id> \
-H "Authorization: Bearer $TOKEN"
curl -X DELETE https://socialcannon.app/api/v1/accounts/<account_id> \
-H "Authorization: Bearer $TOKEN"
Publish immediately (omit scheduledAt) or schedule for later:
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"content": "Your post text here",
"mediaUrls": ["https://example.com/image.jpg"],
"scheduledAt": "2026-04-15T10:00:00Z",
"platformOptions": {
"autoUtm": true
}
}'
Fields:
accountId (required) — ID from the accounts listcontent (required) — post textmediaUrls (optional) — array of public image/video URLsscheduledAt (optional) — ISO 8601 datetime, "optimal" (auto-pick best time based on engagement data, Pro), or omit for immediate publishplatformOptions.autoUtm (optional) — auto-tag URLs with UTM parametersplatformOptions.mediaType (optional) — controls content type:
"reel" — Facebook/Instagram Reel (vertical 9:16 video)"story" — Facebook/Instagram/TikTok Story (24h ephemeral)"short" — YouTube Short (vertical video ≤60s)"community" — YouTube Community post (text/image)platformOptions TikTok fields — TikTok posts require privacyLevel and support disableComment / disableDuet / disableStitch, commercialContent, brandOrganic, brandedContent. See the TikTok section under Platform-Specific Notes.curl "https://socialcannon.app/api/v1/posts?status=published&platform=twitter&limit=20" \
-H "Authorization: Bearer $TOKEN"
Query params: status (draft/scheduled/published/failed), platform, accountId, limit, cursor
curl https://socialcannon.app/api/v1/posts/<post_id> \
-H "Authorization: Bearer $TOKEN"
curl -X PATCH https://socialcannon.app/api/v1/posts/<post_id> \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "Updated text",
"scheduledAt": "2026-04-16T14:00:00Z",
"platformOptions": { "autoUtm": true }
}'
Fields: content, scheduledAt, platformOptions — all optional.
curl -X DELETE https://socialcannon.app/api/v1/posts/<post_id> \
-H "Authorization: Bearer $TOKEN"
If the post is published, this also attempts to delete it from the social platform.
curl -X POST https://socialcannon.app/api/v1/posts/<post_id>/retry \
-H "Authorization: Bearer $TOKEN"
Resets the failed post and attempts to publish immediately. No body needed. If it fails again, the post returns to failed status with the new error.
Create multi-part threads (Twitter reply chains or Instagram carousels):
curl -X POST https://socialcannon.app/api/v1/posts/thread \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"items": [
{ "content": "Thread part 1 — the hook" },
{ "content": "Thread part 2 — the detail" },
{ "content": "Thread part 3 — the CTA", "mediaUrls": ["https://..."] }
],
"scheduledAt": "2026-04-15T10:00:00Z",
"platformOptions": { "autoUtm": true }
}'
Fields: accountId (required), items (required, min 2, max 25), scheduledAt (optional), platformOptions (optional). Instagram requires media on each item.
Upload images/videos before creating posts. Three-step direct-to-GCS flow — bytes go straight to Google Cloud Storage via a signed URL, never through SocialCannon's server. This supports files up to 4GB.
Accepted types: image/jpeg, image/png, image/gif, image/webp, video/mp4, video/quicktime, video/webm.
curl -X POST https://socialcannon.app/api/v1/media/upload-init \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"filename": "photo.jpg",
"contentType": "image/jpeg",
"size": 1048576
}'
Response:
{
"success": true,
"data": {
"uploadUrl": "https://storage.googleapis.com/...?X-Goog-Signature=...",
"publicUrl": "https://storage.googleapis.com/<bucket>/media/<clientId>/<uuid>.jpg",
"requiredHeaders": {
"Content-Type": "image/jpeg",
"x-goog-acl": "public-read"
}
}
}
uploadUrl is a V4 signed PUT URL valid for 15 minutes. size must be the exact byte size of the file you're about to upload.
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/jpeg" \
-H "x-goog-acl: public-read" \
--data-binary @photo.jpg
You must send the exact headers returned in requiredHeaders. Do not send the Authorization header — the signed URL carries its own auth. A successful PUT returns HTTP 200 with an empty body.
curl -X POST https://socialcannon.app/api/v1/media/upload-complete \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "publicUrl": "https://storage.googleapis.com/<bucket>/media/<clientId>/<uuid>.jpg" }'
Pass the publicUrl you got from step 1 verbatim. The server verifies the object exists, stamps customTime (starting the 30-day retention window), and increments your upload quota.
Response: { "success": true, "data": { "url": "https://...", "filename": "media/<clientId>/<uuid>.jpg", "contentType": "image/jpeg", "size": 1048576 } }
Use the returned url in the mediaUrls field when creating posts. Bytes are stored as-is — there is no server-side optimization, so upload the format/quality you want published.
403 on step 1 with code set → your tier has hit its upload quota. Inspect limit in the response body.404 on step 3 → the PUT didn't actually land. Retry from step 2.403 on step 3 → publicUrl doesn't belong to your client. Use the exact URL returned by step 1, do not construct it yourself.See posts grouped by date with gap analysis:
curl "https://socialcannon.app/api/v1/calendar?startDate=2026-04-01&endDate=2026-04-30" \
-H "Authorization: Bearer $TOKEN"
Returns posts, summary (totals by status/platform/day), and gaps (dates with no posts).
Query params: startDate (required), endDate (required), accountId, platform
curl "https://socialcannon.app/api/v1/calendar/slots?startDate=2026-04-01&endDate=2026-04-07&slotDurationMinutes=60" \
-H "Authorization: Bearer $TOKEN"
Query params: startDate (required), endDate (required, max 14-day range), slotDurationMinutes (optional, 30-1440, default 60).
Returns { slots[], totalSlots, availableSlots, occupiedSlots }.
Fetch live engagement metrics from the platform:
curl https://socialcannon.app/api/v1/posts/<post_id>/analytics \
-H "Authorization: Bearer $TOKEN"
Returns: likes, comments, shares, impressions, reach, clicks, engagementRate, plus historical snapshots.
curl "https://socialcannon.app/api/v1/analytics/summary?startDate=2026-04-01&endDate=2026-04-30" \
-H "Authorization: Bearer $TOKEN"
Returns totals across all posts for the date range.
curl -X POST https://socialcannon.app/api/v1/analytics/refresh \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "platform": "twitter", "limit": 20 }'
Fields: postIds (optional, array of up to 50 post IDs to refresh), platform (optional, filter), limit (optional, default 20, max 50). If postIds is provided, those specific posts are refreshed; otherwise recent published posts are refreshed.
curl "https://socialcannon.app/api/v1/engagements?isRead=false&limit=20" \
-H "Authorization: Bearer $TOKEN"
Query params: postId, isRead (true/false), limit, cursor
curl "https://socialcannon.app/api/v1/posts/<post_id>/engagements?cursor=<next_cursor>" \
-H "Authorization: Bearer $TOKEN"
Fetches fresh comments from the platform and stores them. Supports cursor for pagination.
curl -X PATCH https://socialcannon.app/api/v1/engagements/<engagement_id> \
-H "Authorization: Bearer $TOKEN"
Marks the engagement as read. No request body needed — the endpoint auto-marks on PATCH.
curl -X POST https://socialcannon.app/api/v1/engagements/<engagement_id>/reply \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "content": "Thanks for the feedback!" }'
Posts the reply directly on the social platform.
Adapt content for multiple platforms using AI. Two modes available:
curl -X POST https://socialcannon.app/api/v1/posts/repurpose \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sourceContent": "Your long-form content here...",
"targetPlatforms": ["twitter", "facebook", "tiktok"],
"mode": "preview",
"tone": "professional"
}'
Returns { "variants": [{ "platform", "content", "validation", "characterCount" }], "allValid" }.
curl -X POST https://socialcannon.app/api/v1/posts/repurpose \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sourceContent": "Your content here...",
"targetPlatforms": ["twitter", "facebook"],
"mode": "post",
"accountIds": { "twitter": "acc_123", "facebook": "acc_456" },
"mediaUrls": { "twitter": ["https://example.com/video.mp4"] },
"appendContent": { "twitter": "Links or extra text for Twitter only" },
"appendToAll": "Text appended to all platforms"
}'
Returns { "results": [{ "platform", "success", "postUrl?", "error?" }] }.
All content is humanized automatically to remove AI writing patterns. Trusted clients bypass tier limits.
Not available for TikTok. A/B-test variants carry only content + media, so they can't set the per-post privacy level TikTok requires. A TikTok account is rejected with
400andcode: "PLATFORM_UNSUPPORTED"— publish individual posts to TikTok instead.
Not available for LinkedIn. LinkedIn is currently publish-only (no post analytics), so a winner could never be determined. A LinkedIn account is rejected with
400andcode: "PLATFORM_UNSUPPORTED"— publish individual posts to LinkedIn instead.
curl -X POST https://socialcannon.app/api/v1/ab-tests \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"name": "CTA test",
"variants": [
{ "content": "Check out our new feature!", "mediaUrls": ["https://..."] },
{ "content": "You won'\''t believe this new feature..." }
],
"metric": "engagementRate",
"minDurationHours": 24,
"scheduledAt": "2026-04-20T10:00:00Z"
}'
Publish behavior matches POST /api/v1/posts:
scheduledAt → all variants publish immediately to the platform via the social adapterscheduledAt → all variants are scheduled for that time (must be within 30 days; cron publishes them hourly)Each variant is a separate post record. Auto-completes after minDurationHours and the winner is determined by the chosen metric. Per-variant mediaUrls is optional.
Partial failure semantics: if ANY variant fails to publish during immediate mode, the endpoint returns HTTP 502 and the failed variants are marked with status: 'failed'. The A/B test record is still created, but the winner comparison at completion only considers successfully published variants. Inspect each variant's post status before relying on test results.
curl https://socialcannon.app/api/v1/ab-tests/<test_id> \
-H "Authorization: Bearer $TOKEN"
Returns per-variant metrics, current winner, and confidence score.
curl "https://socialcannon.app/api/v1/ab-tests?status=active" \
-H "Authorization: Bearer $TOKEN"
curl -X POST https://socialcannon.app/api/v1/ab-tests/<test_id>/complete \
-H "Authorization: Bearer $TOKEN"
curl "https://socialcannon.app/api/v1/accounts/<account_id>/timing?timezone=UTC-5" \
-H "Authorization: Bearer $TOKEN"
Returns top 5 time slots ranked by average engagement rate with confidence scores.
Combines engagement data with calendar availability:
curl "https://socialcannon.app/api/v1/timing/optimal-slot?accountId=<account_id>&timezone=UTC-5" \
-H "Authorization: Bearer $TOKEN"
Returns the next open slot ranked by historical performance.
Distribute posts across optimal time slots for the next 7 days:
curl -X POST https://socialcannon.app/api/v1/posts/auto-schedule \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"posts": [
{ "content": "Post 1 text" },
{ "content": "Post 2 text", "mediaUrls": ["https://..."] },
{ "content": "Post 3 text" }
],
"timezone": "UTC-5"
}'
Max 20 posts per request. Each post gets a unique slot. Returns { scheduled: [...], unscheduled: [...], summary: {...} }.
Generate UTM-tagged URLs:
curl -X POST https://socialcannon.app/api/v1/links/generate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/product",
"platform": "twitter",
"campaign": "spring-launch",
"content": "hero-cta",
"postId": "<post_id>",
"save": true
}'
Fields: url (required), platform (optional — sets utm_source), campaign (optional — utm_campaign), content (optional — utm_content), term (optional — utm_term), postId (optional — link to a post), save (optional, default true — persist to tracked_links).
curl "https://socialcannon.app/api/v1/links?postId=<post_id>&platform=twitter&limit=20&cursor=<cursor>" \
-H "Authorization: Bearer $TOKEN"
Query params: postId, platform, limit, cursor — all optional.
List supported platforms and their capabilities (public, no auth required):
curl https://socialcannon.app/api/v1/platforms
platformOptions.mediaType to "reel". Video must be MP4/MOV, vertical (9:16). Without this, videos post as regular video posts.platformOptions.mediaType to "story". Supports one image or video. Ephemeral (24h).curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"content": "Check out this tutorial!",
"mediaUrls": ["https://example.com/video.mp4"],
"platformOptions": {
"mediaType": "reel"
}
}'
platformOptions.mediaType to "story". One image or video.platformOptions.mediaType to "reel". Vertical 9:16 video.platformOptions.privacyLevel is REQUIRED on every TikTok post — there is no default. Omitting it returns 400 with code TIKTOK_PRIVACY_REQUIRED. Use a value from the creator-info endpoint's privacyLevelOptions (e.g. PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY).disableComment, disableDuet, disableStitch (booleans). Duet/Stitch apply to video only. If the creator-info endpoint reports an interaction is disabled account-side (commentDisabled / duetDisabled / stitchDisabled), set the matching disable* to true (the server force-disables it regardless).commercialContent: true if the post promotes a brand, product, or service, then set brandOrganic: true (your own brand) and/or brandedContent: true (paid/third-party partnership). If brandedContent is true, privacyLevel cannot be SELF_ONLY.platformOptions.mediaType to "story". One video. Ephemeral (24h).Get a TikTok account's posting capabilities — call this before composing a TikTok post; it returns the allowed privacy levels and which interactions are disabled:
curl https://socialcannon.app/api/v1/accounts/<account_id>/tiktok/creator-info \
-H "Authorization: Bearer $TOKEN"
Returns { creatorNickname, creatorUsername, creatorAvatarUrl, privacyLevelOptions, commentDisabled, duetDisabled, stitchDisabled, maxVideoPostDurationSec }. Choose privacyLevel from privacyLevelOptions and respect the *Disabled flags.
Example — publish a public TikTok video with comments on:
curl -X POST https://socialcannon.app/api/v1/posts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"accountId": "<account_id>",
"content": "New track out now!",
"mediaUrls": ["https://example.com/clip.mp4"],
"platformOptions": {
"privacyLevel": "PUBLIC_TO_EVERYONE",
"disableComment": false,
"disableDuet": false,
"disableStitch": false
}
}'
platformOptions.mediaType to "short". Vertical video ≤60s.platformOptions.mediaType to "community". Text/image post to channel's Community tab.publishAt timestamp.429 with Retry-After header when exceededFour tiers. Twitter/X is the only platform with a per-post API cost passed through, so every tier carries an X-write quota.
sales@socialcannon.appAny tier exceeding its maxTwitterPostsPerPeriod cap returns:
{
"success": false,
"error": "Twitter/X post limit reached (50/50). Upgrade to Pro for unlimited Twitter publishing.",
"code": "LIMIT_EXCEEDED",
"limit": { "type": "twitter_posts_per_period", "current": 50, "max": 50, "tier": "pro" }
}
HTTP 403. Resets at the start of the next billing period.
If you run into issues with the API, account connections, or integration setup, contact support@socialcannon.app.
accountId values before creating posts.autoUtm: true in platformOptions to automatically tag URLs in posts.validation field — if valid is false, adjust the content before publishing.scheduledAt: "optimal" to let SocialCannon pick the best posting time automatically (Pro).mediaType to "short" for Shorts or "community" for Community tab posts.GET /api/v1/accounts/{id}/tiktok/creator-info first — it returns the allowed privacyLevelOptions (pass one as platformOptions.privacyLevel; it is required) and which of Comment/Duet/Stitch are disabled (never enable a disabled one).