BackSocialCannon API Docs
Get Started

API Reference

SocialCannon provides a REST API for publishing to 6 social platforms (Twitter, Facebook, Instagram, LinkedIn, TikTok, YouTube), analytics, engagement monitoring, A/B testing, AI-powered content repurposing, and 23 MCP tools for AI agents. OpenClaw ready.

Quick Start

1. Get an API token
curl -X POST https://socialcannon.app/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d '{ "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_SECRET" }'
2. Create a post
curl -X POST https://socialcannon.app/api/v1/posts \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "ACCOUNT_ID",
    "content": "Hello from SocialCannon!"
  }'
3. Post with an image

The mediaUrls field accepts any public URL — you can use an image already hosted elsewhere, or upload via the media endpoint first.

Option A: Use an external URL directly

curl -X POST https://socialcannon.app/api/v1/posts \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "ACCOUNT_ID",
    "content": "Check out this photo!",
    "mediaUrls": ["https://example.com/photo.jpg"]
  }'

Option B: Upload via signed URL (direct-to-GCS)

# Step 1: Ask for a signed PUT URL
curl -X POST https://socialcannon.app/api/v1/media/upload-init \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "filename": "photo.jpg", "contentType": "image/jpeg", "size": 123456 }'
# Returns: { "success": true, "data": { "uploadUrl": "...", "publicUrl": "...", "requiredHeaders": { ... } } }

# Step 2: PUT the bytes directly to GCS (file size up to 4GB)
curl -X PUT "<uploadUrl>" \
  -H "Content-Type: image/jpeg" \
  -H "x-goog-acl: public-read" \
  --data-binary "@photo.jpg"

# Step 3: Finalize so usage counter increments and lifecycle clock starts
curl -X POST https://socialcannon.app/api/v1/media/upload-complete \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "publicUrl": "<publicUrl from step 1>" }'

# Step 4: Use the publicUrl when creating the post
curl -X POST https://socialcannon.app/api/v1/posts \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "ACCOUNT_ID",
    "content": "Check out this photo!",
    "mediaUrls": ["<publicUrl>"]
  }'

Bytes go straight to Google Cloud Storage — SocialCannon never touches them, so files up to 4GB upload cleanly. Up to 4 images for Twitter, 10 for Instagram carousels.

4. Schedule for later
curl -X POST https://socialcannon.app/api/v1/posts \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "ACCOUNT_ID",
    "content": "Scheduled post!",
    "scheduledAt": "2026-04-01T10:00:00Z"
  }'

Authentication

SocialCannon uses OAuth2 client_credentials flow. Exchange yourclient_id andclient_secret for a JWT token that expires in 1 hour.

Include the token in all authenticated requests:

Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

Available scopes:

posts:readposts:writeposts:deleteaccounts:readaccounts:writemedia:uploadanalytics:readlinks:readlinks:writeengagements:readengagements:write

Rate limiting:

Free: 30 requests/minute. Pro: 300 requests/minute. Returns 429 with Retry-After header when exceeded.

Subscription Tiers

Free

2 connected accounts

10 posts/period, 2 scheduled

3 Twitter/X posts per period (hard cap)

Twitter, Facebook

No analytics, no replies

2-item threads, 5 tracked links

3 media uploads/period

30 API requests/min

Pro — $15/mo

Unlimited accounts & posts (free platforms)

50 Twitter/X posts per period

All 4 platforms unlocked

Full analytics with history

25-item threads, unlimited tracked links

Engagement replies, A/B testing (5×4)

Timing suggestions, 90-day calendar

300 API requests/min

Agency — $49/mo

Everything in Pro

250 Twitter/X posts per period

A/B testing (20 concurrent, 6 variants)

365-day calendar

Priority support

600 API requests/min

Enterprise — custom

Custom Twitter/X quota

Custom rate limits + SLA

Dedicated support

Custom integrations

1200 API requests/min default

Contact sales

Response Format

Success
{
  "success": true,
  "data": { ... }
}
Error
{
  "success": false,
  "error": "Description of the error",
  "code": "LIMIT_EXCEEDED",
  "details": { ... }
}

REST API Endpoints

Quick Start

INFO
Getting started — publish your first postPublic

1. Get your client_id and client_secret from the SocialCannon dashboard 2. Get a token: POST /api/v1/auth/token with your credentials 3. Connect a social account: redirect user to /api/connect/:platform?client_id=YOUR_ID 4. List connected accounts: GET /api/v1/accounts (use the account id from the response) 5. Publish a post: POST /api/v1/posts with accountId, content, and optional mediaUrls

// Example: publish a text post to a connected account
POST /api/v1/posts
Authorization: Bearer YOUR_TOKEN

{
  "accountId": "your-account-id",
  "content": "Hello from SocialCannon!"
}

// Response:
{
  "success": true,
  "data": {
    "id": "post-id",
    "status": "published",
    "platformPostId": "...",
    "platformPostUrl": "https://..."
  }
}
INFO
Platform capabilitiesPublic

Each platform supports different content types. Use the mediaType field in platformOptions to control how content is published.

Facebook:
  • Feed post (text, images, or video)
  • Story (single image or video, mediaType: "story")
  • Reel (single video, mediaType: "reel")

Instagram:
  • Feed post (images required, up to 10 for carousel)
  • Story (single image or video, mediaType: "story")
  • Reel (single video, mediaType: "reel")

Twitter/X:
  • Tweet (text, optional images or video)

TikTok:
  • Video post (single video required)
  • Photo carousel (multiple images, up to 35)
  • Story (single image or video, mediaType: "story")
  • privacyLevel required on all posts (use get_tiktok_creator_info to get allowed values)

YouTube:
  • Video upload (single video, mediaType: "feed")
  • Short (single vertical video ≤60s, mediaType: "short")
  • Community post (text only, mediaType: "community")

Authentication

POST
/api/v1/auth/tokenPublic

Exchange client credentials for a JWT access token (OAuth2 client_credentials flow).

{ "client_id": "...", "client_secret": "sc_..." }
Response:
{ "access_token": "eyJ...", "token_type": "bearer", "expires_in": 3600 }
GET
/api/v1/auth/clients

List all API clients.

Scope: accounts:read
POST
/api/v1/auth/clients

Create a new API client. Returns the secret once.

Scope: clients:manage
{ "name": "My Client", "scopes": ["posts:read", "posts:write"] }

Posts

GET
/api/v1/posts

List posts. Supports cursor-based pagination.

Scope: posts:read
Query params: status, platform, accountId, limit, cursor
POST
/api/v1/posts

Create a post. Omit scheduledAt to publish immediately. Media URLs must be publicly accessible. Maximum scheduling window: 30 days.

Scope: posts:write
{
  "accountId": "abc123",
  "content": "Hello world!",
  "mediaUrls": ["https://example.com/video.mp4"],
  "scheduledAt": "2026-04-01T10:00:00Z",
  "platformOptions": {
    "mediaType": "reel"
  }
}
Response:
// Success:
{ "success": true, "data": { "id": "...", "status": "published", "platformPostId": "...", "platformPostUrl": "..." } }

// Error:
{ "success": false, "error": "Publishing failed: ..." }
GET
/api/v1/posts/:id

Get a single post by ID.

Scope: posts:read
PATCH
/api/v1/posts/:id

Update a draft or scheduled post.

Scope: posts:write
{ "content": "Updated text", "scheduledAt": "..." }
DELETE
/api/v1/posts/:id

Delete a post. If published, also attempts platform-side deletion.

Scope: posts:delete

Threads

POST
/api/v1/posts/thread

Create a multi-part thread (Twitter thread or Instagram carousel). Publishes immediately or schedules for later. Maximum scheduling window: 30 days.

Scope: posts:write
{
  "accountId": "abc123",
  "items": [
    { "content": "Thread part 1..." },
    { "content": "Thread part 2...", "mediaUrls": ["https://..."] }
  ],
  "scheduledAt": "2026-04-01T10:00:00Z"
}

Accounts

GET
/api/v1/accounts

List all connected social media accounts.

Scope: accounts:read
GET
/api/v1/accounts/:id

Get a single account. Tokens are stripped from the response.

Scope: accounts:read
DELETE
/api/v1/accounts/:id

Disconnect a social account.

Scope: accounts:write
GET
/api/v1/accounts/:id/tiktok/creator-info

TikTok only. Returns the account's live posting capabilities for building a compliant post UI: allowed privacy levels (`privacyLevelOptions`), whether Comment/Duet/Stitch are disabled, the creator nickname/username, and `maxVideoPostDurationSec`.

Scope: accounts:read
Response:
{ "success": true, "data": { "creatorNickname": "...", "creatorUsername": "...", "creatorAvatarUrl": "...", "privacyLevelOptions": ["PUBLIC_TO_EVERYONE","SELF_ONLY"], "commentDisabled": false, "duetDisabled": false, "stitchDisabled": false, "maxVideoPostDurationSec": 600 } }

Media

POST
/api/v1/media/upload-init

Step 1 — initialize a direct-to-GCS upload. Returns a 15-minute V4 signed PUT URL plus the final public URL. Enforces subscription quota before issuing the URL.

Scope: media:upload
{ "filename": "clip.mp4", "contentType": "video/mp4", "size": 12345678 }
Response:
{ "data": { "uploadUrl": "https://storage.googleapis.com/...", "publicUrl": "https://storage.googleapis.com/...", "requiredHeaders": { "Content-Type": "video/mp4", "x-goog-acl": "public-read" } } }
PUT
{uploadUrl}Public

Step 2 — upload the file bytes directly to GCS using the signed URL from step 1. Must include the headers from `requiredHeaders` exactly or the signature check fails. Supports files up to 4GB.

Raw binary file contents
Response:
200 OK (no body)
POST
/api/v1/media/upload-complete

Step 3 — finalize the upload: stamps the lifecycle retention timer (customTime) and increments your `mediaUploadsThisPeriod` counter. Verifies the object exists in your client prefix before bookkeeping.

Scope: media:upload
{ "publicUrl": "https://storage.googleapis.com/.../media/{clientId}/{uuid}.{ext}" }
Response:
{ "data": { "url": "...", "filename": "...", "contentType": "...", "size": 102400 } }

Calendar

GET
/api/v1/calendar

Get a content calendar view with posts, gap analysis, and summary stats for a date range (max 90 days).

Scope: posts:read
Query params: startDate (required), endDate (required), accountId?, platform?
Response:
{ "posts": [...], "summary": { "totalPosts", "postsByPlatform", "postsByDay", "gaps": [{ "date", "dayOfWeek" }] } }
GET
/api/v1/calendar/slots

Find available time slots not occupied by scheduled or published posts (max 14-day range).

Scope: posts:read
Query params: startDate (required), endDate (required), slotDurationMinutes? (30-1440, default: 60)
Response:
{ "slots": [{ "start", "end", "available" }], "totalSlots", "availableSlots", "occupiedSlots" }

Analytics

GET
/api/v1/posts/:id/analytics

Fetch live engagement metrics from the platform, store a snapshot, and return current + history.

Scope: posts:read
Response:
{ "current": { "metrics": { "likes", "comments", "shares", "impressions", ... }, "fetchedAt" }, "history": [...] }
GET
/api/v1/analytics/summary

Get aggregate analytics across all posts for a date range.

Scope: posts:read
Query params: startDate (required), endDate (required), platform?, accountId?
Response:
{ "totalPosts", "totalLikes", "totalComments", "totalShares", "totalImpressions", "avgEngagementRate" }
POST
/api/v1/analytics/refresh

Bulk-refresh analytics for multiple posts (max 50 per request).

Scope: posts:read
{ "postIds?": ["..."], "platform?": "twitter", "limit?": 20 }
Response:
{ "refreshed": 15, "failed": 2, "results": [{ "postId", "success", "error?" }] }

Links & UTM

GET
/api/v1/links

List tracked links with UTM parameters. Supports cursor-based pagination.

Scope: links:read
Query params: postId?, platform?, limit?, cursor?
POST
/api/v1/links/generate

Generate a UTM-tagged URL. Optionally save it for tracking.

Scope: links:write
{ "url": "https://example.com", "platform?": "twitter", "campaign?": "launch", "content?": "cta-btn", "postId?": "...", "save?": true }
Response:
{ "originalUrl": "https://example.com", "trackedUrl": "https://example.com?utm_source=twitter&...", "linkId?": "..." }

Engagements

GET
/api/v1/posts/:id/engagements

Fetch comments and replies from the platform for a published post. Stores new engagements.

Scope: engagements:read
Query params: cursor?
Response:
{ "engagements": [{ "type", "authorName", "content", "createdAt", ... }], "nextCursor?" }
GET
/api/v1/engagements

List all stored engagements across posts. Filter by read status for an inbox view.

Scope: engagements:read
Query params: postId?, isRead? (true/false), limit?, cursor?
PATCH
/api/v1/engagements/:id

Mark an engagement as read.

Scope: engagements:write
POST
/api/v1/engagements/:id/reply

Reply to a comment or mention directly on the social platform.

Scope: engagements:write
{ "content": "Thanks for the feedback!" }
Response:
{ "platformReplyId": "...", "engagementId": "...", "repliedAt": "..." }

Content Repurposing

POST
/api/v1/posts/repurpose

Use AI to adapt content for multiple platforms. Two modes: "preview" returns adapted variants for review, "post" adapts and publishes in one call. Content is humanized to remove AI writing patterns. Trusted clients bypass tier limits.

Scope: posts:write
{
  "sourceContent": "Your original text...",
  "targetPlatforms": ["twitter", "facebook", "tiktok"],
  "mode?": "preview",
  "tone?": "professional",
  "accountIds?": { "twitter": "acc_1", "facebook": "acc_2" },
  "mediaUrls?": { "twitter": ["https://..."] },
  "appendContent?": { "twitter": "Extra text for Twitter" },
  "appendToAll?": "Appended to all platforms"
}
Response:
preview: { "variants": [{ "platform", "content", "validation", "characterCount" }], "allValid" }
post: { "results": [{ "platform", "success", "postUrl?", "error?" }] }

A/B Testing

Pro
POST
/api/v1/ab-tests

Create an A/B test. Behavior matches POST /api/v1/posts: if scheduledAt is omitted, all variants publish immediately to the platform; if scheduledAt is provided, all variants are scheduled for that time (must be within 30 days). Each variant is a separate post record. Auto-completes after minDurationHours. If ANY variant fails to publish during immediate mode, the endpoint returns 502 and the partial results are marked as failed — the winning-metric comparison at completion will only consider published variants. Per-variant mediaUrls optional. Not supported for TikTok accounts (A/B variants cannot set the privacy level TikTok requires) — returns 400 with code PLATFORM_UNSUPPORTED.

Scope: posts:write
{
  "accountId": "abc123",
  "name": "CTA test",
  "variants": [
    { "content": "Check out our new feature!", "mediaUrls": ["https://..."] },
    { "content": "You won't believe this new feature..." }
  ],
  "metric?": "engagementRate | likes | impressions | clicks",
  "minDurationHours?": 24,
  "scheduledAt?": "2026-04-20T10:00:00Z"
}
GET
/api/v1/ab-tests

List A/B tests. Filter by status.

Scope: posts:read
Query params: status? (active/completed/canceled), limit?, cursor?
GET
/api/v1/ab-tests/:id

Get test details with live per-variant analytics and current winner.

Scope: posts:read
Response:
{ "variants", "variantResults": [{ "variantId", "metrics" }], "currentWinner", "confidence" }
POST
/api/v1/ab-tests/:id/complete

Force-complete an active test and determine the winner.

Scope: posts:write
DELETE
/api/v1/ab-tests/:id

Cancel an active test. Variant posts remain as regular posts.

Scope: posts:write

Timing Suggestions

Pro
GET
/api/v1/accounts/:id/timing

Analyze posting history and engagement to recommend optimal days and hours for posting.

Scope: analytics:read
Query params: timezone? (UTC, UTC+5, UTC-8 — default: UTC), platform?
Response:
{ "suggestions": [{ "day", "hour", "engagementRate", "sampleSize" }], "timezone" }

Platforms

GET
/api/v1/platformsPublic

List all supported platforms and their capabilities. Public endpoint, no auth required.

Platform Options

INFO
platformOptions referencePublic

The platformOptions field in POST /api/v1/posts accepts platform-specific options. Use mediaType to control how content is published.

mediaType values:
• "reel" — Facebook or Instagram Reel (single video, MP4/MOV)
• "story" — Facebook, Instagram, or TikTok Story (single media, 24h ephemeral)
• "short" — YouTube Short (single video, vertical ≤60s)
• "community" — YouTube Community tab post (text only)

Media rules:
• Stories, Reels, and Shorts accept only one media attachment
• Facebook/Instagram feed posts accept multiple images (carousel, max 10)
• TikTok photo carousel accepts up to 35 images
• TikTok video posts accept only one video
• YouTube videos/shorts accept only one video file
• All media URLs must be publicly accessible

YouTube-specific options:
• "title" — Video title (max 100 chars)
• "tags" — Comma-separated video tags
• "categoryId" — YouTube category ID (default: "10" Music)
• "privacyStatus" — "public", "unlisted", or "private"

TikTok-specific options (call GET /api/v1/accounts/:id/tiktok/creator-info first):
• "privacyLevel" — REQUIRED. One of: PUBLIC_TO_EVERYONE, MUTUAL_FOLLOW_FRIENDS, FOLLOWER_OF_CREATOR, SELF_ONLY. Choose from privacyLevelOptions returned by creator-info. Omitting this field returns 400 TIKTOK_PRIVACY_REQUIRED.
• "disableComment" — boolean (default: false). Set true to disable comments. Must be true if commentDisabled is true on creator-info.
• "disableDuet" — boolean (default: false). Video only. Set true to disable duets. Must be true if duetDisabled is true on creator-info.
• "disableStitch" — boolean (default: false). Video only. Set true to disable stitches. Must be true if stitchDisabled is true on creator-info.
• "commercialContent" — boolean. Set true if the post promotes a brand, product, or service (enables commercial content disclosure).
• "brandOrganic" — boolean. Set true if promoting your own brand ("Your Brand" disclosure). Requires commercialContent: true.
• "brandedContent" — boolean. Set true for paid/third-party partnerships. Requires commercialContent: true. Cannot be combined with privacyLevel: SELF_ONLY.

OAuth Connect

GET
/api/connect/:platformPublic

Initiate OAuth connection for a social platform. Redirects to the platform's authorization page. Supported platforms: twitter, facebook, instagram, linkedin, tiktok, youtube.

Query params: client_id (required), redirect_to (optional callback URL)
GET
/api/connect/:platform/callbackPublic

OAuth callback handler. Exchanges authorization code for tokens and stores the connected account. Do not call directly — the platform redirects here after authorization.

Available Scopes

INFO
Scope referencePublic

Scopes control what an API client can access. Assign scopes when creating a client via POST /api/v1/auth/clients.

posts:read — List and view posts
posts:write — Create, update, schedule posts
posts:delete — Delete posts
accounts:read — List connected accounts
accounts:write — Connect/disconnect social accounts
media:upload — Upload media files
analytics:read — View analytics and timing suggestions
engagements:read — View comments and replies
engagements:write — Reply to engagements, mark as read
links:read — View tracked links
links:write — Generate UTM-tagged links
clients:manage — Create and list API clients

Connecting Social Accounts

Social accounts are connected via browser-based OAuth flows. Redirect users to the connect endpoint for the desired platform:

PlatformConnect URLNotes
Twitter / X/api/connect/twitter?client_id=...OAuth2 with PKCE
Facebook/api/connect/facebook?client_id=...Page-level access
Instagram/api/connect/instagram?client_id=...Requires media (no text-only)
LinkedIn/api/connect/linkedin?client_id=...Post-only (no analytics/engagement)

After authorization, the user is redirected back to your app with the account connected. Tokens are encrypted with AES-256-GCM before storage.

Post Lifecycle

draftscheduledpublishingpublished
&searr;failed(after 3 retries with 5min backoff)
  • No scheduledAt → immediate publish
  • With scheduledAt → scheduled, picked up by cron
  • Deleting a published post also attempts platform-side deletion
  • Thread posts follow the same lifecycle (each item published sequentially)

Platform-Specific Notes

Twitter / X

280 character limit

Up to 4 images per tweet

Threads via reply chains

v2 API for tweets, v1.1 for media upload

Facebook

63,206 character limit

Page-level access tokens

Supports native scheduling via API

Full engagement + analytics support

Instagram

2,200 character limit

Requires media (no text-only posts)

Max 10 carousel items, no API deletion

Threads use carousel container model

LinkedIn

3,000 character limit

Text-only or single/multi-image posts

Threads combine items into one post

Post-only (no analytics or engagement)

MCP Server (for AI Agents)

SocialCannon includes an MCP server with 23 tools that expose all functionality for AI agents (Claude, GPT, etc.) via the @modelcontextprotocol/sdk over stdio transport.

Start the MCP server (repo):

npm run mcp

Or run the published package directly (external clients):

npx -y @socialcannon/mcp

Set SOCIALCANNON_CLIENT_ID and SOCIALCANNON_CLIENT_SECRET (from your dashboard) before running.

Configuration (preferred — auto-refreshes JWT):

MCP_CLIENT_ID=<your API client ID>
MCP_CLIENT_SECRET=<your API client secret>
SOCIALCANNON_API_URL=https://socialcannon.app

Alternatively, set MCP_API_TOKEN with a pre-generated JWT (expires after 1 hour, no auto-refresh).

Available Tools (22)

Core

list_platforms

List all supported social media platforms and their capabilities.

Params: None

list_accounts

List all connected social media accounts.

Params: platform?

get_tiktok_creator_info

Fetch a TikTok account's posting capabilities — allowed privacy levels, disabled interactions, nickname, max video duration. Call before create_post for TikTok.

Params: accountId

create_post

Create and publish or schedule a social media post. For TikTok, privacyLevel is required (from get_tiktok_creator_info); also supports disableComment, disableDuet (video), disableStitch (video), commercialContent, brandOrganic, brandedContent.

Params: accountId, content, mediaUrls?, scheduledAt?, visibility?, privacyLevel?, disableComment?, disableDuet?, disableStitch?, commercialContent?, brandOrganic?, brandedContent?

list_posts

List social media posts with optional filters.

Params: status?, platform?, accountId?, limit?

get_post

Get details of a specific post.

Params: postId

update_post

Update a draft or scheduled post.

Params: postId, content?, scheduledAt?

delete_post

Delete a post (also removes from platform if published).

Params: postId

Calendar & Analytics

get_calendar

Get a content calendar view with posts, gaps, and summary stats.

Params: startDate, endDate, accountId?, platform?

get_post_analytics

Get engagement metrics (likes, comments, shares, impressions) for a post.

Params: postId

get_analytics_summary

Get aggregate analytics across all posts for a date range.

Params: startDate, endDate, platform?, accountId?

Threads & Links

create_thread

Create a multi-part thread (Twitter thread or Instagram carousel).

Params: accountId, items[], scheduledAt?

generate_tracked_link

Generate a UTM-tagged URL for tracking social media traffic.

Params: url, platform?, campaign?, content?, postId?, save?

Engagements

get_engagements

List stored engagements (comments/replies) with optional filters.

Params: postId?, isRead?, limit?, cursor?

reply_to_engagement

Reply to a comment/reply directly on the social platform.

Params: engagementId, content

list_unread_engagements

List all unread engagements (inbox shortcut).

Params: limit?

Content

repurpose_content

Repurpose content for different platforms using AI. Supports preview mode (returns variants) and post mode (adapts and publishes). Content is humanized automatically.

Params: sourceContent, targetPlatforms[], mode?, tone?, accountIds?, mediaUrls?, appendContent?, appendToAll?

A/B Testing & Timing

create_ab_test

Create an A/B test with content variants for an account.

Params: accountId, name, variants[], metric?, minDurationHours?

get_ab_test_results

Get A/B test results with per-variant analytics and winner.

Params: testId

get_best_posting_times

Get recommended posting times based on historical engagement data.

Params: accountId, timezone?

get_optimal_slot

Find the best available time slot for a new post based on calendar gaps and engagement data.

Params: accountId, startDate?, endDate?

auto_schedule_week

Auto-fill a week with optimally timed posts across your connected accounts.

Params: accountId, startDate, posts[]

Hermes Agent Integration

Hermes Agent (Nous Research) ships a first-class MCP client. Point it at the published @socialcannon/mcp package and all 23 tools load over npx — no repo clone.

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"

Use the SOCIALCANNON_CLIENT_ID / SOCIALCANNON_CLIENT_SECRET from your dashboard. Run /reload-mcp in your Hermes session and the tools load automatically (Hermes also auto-reloads config edits within ~30s). Other MCP hosts share the same command, args, and env — Claude Desktop uses an equivalent JSON block (mcpServers) in claude_desktop_config.json.

OpenClaw Integration

SocialCannon is available as an OpenClaw skill. Install it from ClawHub so your AI assistant can publish, schedule, analyze, and manage social media posts through natural conversation.

Install from ClawHub
openclaw skills install socialcannon
Configure credentials

Add your SocialCannon API credentials to your openclaw.json config:

{
  "skills": {
    "entries": {
      "socialcannon": {
        "enabled": true,
        "env": {
          "SOCIALCANNON_CLIENT_ID": "your-client-id",
          "SOCIALCANNON_CLIENT_SECRET": "your-client-secret"
        }
      }
    }
  }
}
SKILL.md reference

If you prefer manual installation, create this file at ~/.openclaw/workspace/skills/socialcannon/SKILL.md:

---
name: socialcannon
description: >
  Publish, schedule, and manage social media posts across
  Twitter/X, Facebook, Instagram, LinkedIn, TikTok, and YouTube.
  Includes content calendar, analytics, A/B testing,
  engagement inbox, AI content repurposing, and timing suggestions.
version: 1.0.0
metadata:
  openclaw:
    requires:
      env:
        - SOCIALCANNON_CLIENT_ID
        - SOCIALCANNON_CLIENT_SECRET
      bins:
        - curl
    primaryEnv: SOCIALCANNON_CLIENT_ID
    emoji: "\U0001F4E3"
    homepage: https://socialcannon.app
---

# SocialCannon

Social media publishing API with 23 MCP tools.

## Authentication

First, get a JWT token:

```bash
curl -X POST https://socialcannon.app/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -d "{
    \"client_id\": \"$SOCIALCANNON_CLIENT_ID\",
    \"client_secret\": \"$SOCIALCANNON_CLIENT_SECRET\"
  }"
```

Use the returned access_token as Bearer token for all
subsequent requests. Tokens expire after 1 hour.

## Core Operations

- POST /api/v1/posts — Create/publish a post
- GET /api/v1/posts — List posts (cursor pagination)
- POST /api/v1/posts/thread — Create thread/carousel
- GET /api/v1/calendar — Content calendar with gaps
- GET /api/v1/posts/:id/analytics — Engagement metrics
- GET /api/v1/engagements — Comment inbox
- POST /api/v1/engagements/:id/reply — Reply to comment
- POST /api/v1/posts/repurpose — AI content adaptation
- POST /api/v1/ab-tests — Create A/B test
- GET /api/v1/accounts/:id/timing — Best posting times
- POST /api/v1/links/generate — UTM-tagged URL

Bootstrap & Setup

Create your first API client:

npx tsx scripts/create-client.ts "My App"

This writes directly to Firestore and prints the client_id andclient_secret. Save the secret — it's only shown once.

Or use the dashboard:

Sign in with Google at /sign-in and an API client is automatically provisioned for your account.

Stuck on something? Reach out at support@socialcannon.app