{
  "schemaVersion": "1.0",
  "item": {
    "slug": "publora",
    "name": "Publora",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/sergebulaev/publora",
    "canonicalUrl": "https://clawhub.ai/sergebulaev/publora",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/publora",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=publora",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T16:55:25.780Z",
      "expiresAt": "2026-05-07T16:55:25.780Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=network",
        "contentDisposition": "attachment; filename=\"network-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/publora"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/publora",
    "agentPageUrl": "https://openagent3.xyz/skills/publora/agent",
    "manifestUrl": "https://openagent3.xyz/skills/publora/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/publora/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Publora API — Core Skill",
        "body": "Publora is an affordable REST API for scheduling and publishing social media posts\nacross 11 platforms. Base URL: https://api.publora.com/api/v1"
      },
      {
        "title": "Authentication",
        "body": "All requests require the x-publora-key header. Keys start with sk_.\n\ncurl https://api.publora.com/api/v1/platform-connections \\\n  -H \"x-publora-key: sk_YOUR_KEY\"\n\nGet your key: publora.com → Settings → API Keys → Generate API Key.\n⚠️ Copy immediately — shown only once."
      },
      {
        "title": "Step 0: Get Platform IDs",
        "body": "Always call this first to get valid platform IDs before posting.\n\nconst res = await fetch('https://api.publora.com/api/v1/platform-connections', {\n  headers: { 'x-publora-key': 'sk_YOUR_KEY' }\n});\nconst { connections } = await res.json();\n// connections[i].platformId → e.g. \"linkedin-ABC123\", \"twitter-456\"\n// Also returns: tokenStatus, tokenExpiresIn, lastSuccessfulPost, lastError\n\nPlatform IDs look like: twitter-123, linkedin-ABC, instagram-456, threads-789, etc."
      },
      {
        "title": "Post Immediately",
        "body": "Omit scheduledTime to publish right away:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'Your post content here',\n    platforms: ['twitter-123', 'linkedin-ABC']\n  })\n});"
      },
      {
        "title": "Schedule a Post",
        "body": "Include scheduledTime in ISO 8601 UTC — must be in the future:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'Scheduled post content',\n    platforms: ['twitter-123', 'linkedin-ABC'],\n    scheduledTime: '2026-03-16T10:00:00.000Z'\n  })\n});\n// Response: { postGroupId: \"pg_abc123\", scheduledTime: \"...\" }"
      },
      {
        "title": "Save as Draft",
        "body": "Omit scheduledTime — post is created as draft. Schedule it later:\n\n// Create draft\nconst { postGroupId } = await createPost({ content, platforms });\n\n// Schedule later\nawait fetch(`https://api.publora.com/api/v1/update-post/${postGroupId}`, {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({ status: 'scheduled', scheduledTime: '2026-03-16T10:00:00.000Z' })\n});"
      },
      {
        "title": "List Posts",
        "body": "Filter, paginate and sort your scheduled/published posts:\n\n// GET /api/v1/list-posts\n// Query params: status, platform, fromDate, toDate, page, limit, sortBy, sortOrder\nconst res = await fetch(\n  'https://api.publora.com/api/v1/list-posts?status=scheduled&platform=twitter&page=1&limit=20',\n  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\nconst { posts, pagination } = await res.json();\n// pagination: { page, limit, totalItems, totalPages, hasNextPage, hasPrevPage }\n\nValid statuses: draft, scheduled, published, failed, partially_published"
      },
      {
        "title": "Get / Delete a Post",
        "body": "# Get post details\nGET /api/v1/get-post/:postGroupId\n\n# Delete post (also removes media from storage)\nDELETE /api/v1/delete-post/:postGroupId"
      },
      {
        "title": "Get Post Logs",
        "body": "Debug failed or partially published posts:\n\nconst res = await fetch(\n  `https://api.publora.com/api/v1/post-logs/${postGroupId}`,\n  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\nconst { logs } = await res.json();"
      },
      {
        "title": "Test a Connection",
        "body": "Verify a platform connection is healthy before posting:\n\nconst res = await fetch(\n  'https://api.publora.com/api/v1/test-connection/linkedin-ABC123',\n  { method: 'POST', headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\n// Returns: { status: \"ok\"|\"error\", message, permissions, tokenExpiresIn }"
      },
      {
        "title": "Bulk Schedule (a Week of Content)",
        "body": "from datetime import datetime, timedelta, timezone\nimport requests\n\nHEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }\nbase_date = datetime(2026, 3, 16, 10, 0, 0, tzinfo=timezone.utc)\n\nposts = ['Monday post', 'Tuesday post', 'Wednesday post', 'Thursday post', 'Friday post']\n\nfor i, content in enumerate(posts):\n    scheduled_time = base_date + timedelta(days=i)\n    requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={\n        'content': content,\n        'platforms': ['twitter-123', 'linkedin-ABC'],\n        'scheduledTime': scheduled_time.isoformat()\n    })"
      },
      {
        "title": "Media Uploads",
        "body": "All media (images and videos) use a 3-step pre-signed upload workflow:\n\nStep 1: POST /api/v1/create-post → get postGroupId\nStep 2: POST /api/v1/get-upload-url → get uploadUrl\nStep 3: PUT {uploadUrl} with file bytes (no auth needed for S3)\n\nimport requests\n\nHEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }\n\n# Step 1: Create post\npost = requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={\n    'content': 'Check this out!',\n    'platforms': ['instagram-456'],\n    'scheduledTime': '2026-03-15T14:30:00.000Z'\n}).json()\npost_group_id = post['postGroupId']\n\n# Step 2: Get pre-signed upload URL\nupload = requests.post('https://api.publora.com/api/v1/get-upload-url', headers=HEADERS, json={\n    'fileName': 'photo.jpg',\n    'contentType': 'image/jpeg',\n    'type': 'image',  # or 'video'\n    'postGroupId': post_group_id\n}).json()\n\n# Step 3: Upload directly to S3 (no auth header needed)\nwith open('./photo.jpg', 'rb') as f:\n    requests.put(upload['uploadUrl'], headers={'Content-Type': 'image/jpeg'}, data=f)\n\nFor carousels: call get-upload-url N times with the same postGroupId."
      },
      {
        "title": "Cross-Platform Threading",
        "body": "X/Twitter and Threads support auto-threading. Separate segments with --- on its own line:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'First tweet in the thread.\\n\\n---\\n\\nSecond tweet continues.\\n\\n---\\n\\nFinal tweet wraps up.',\n    platforms: ['twitter-123', 'threads-789']\n  })\n});\n\n⚠️ Threads Restriction: Multi-threaded nested posts (content auto-split into connected replies) are temporarily unavailable on Threads due to Threads app reconnection status. Single posts and carousels continue to work normally. Contact support@publora.com for updates."
      },
      {
        "title": "LinkedIn Analytics",
        "body": "// Post statistics\nawait fetch('https://api.publora.com/api/v1/linkedin-post-statistics', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    postedId: 'urn:li:share:7123456789',\n    platformId: 'linkedin-ABC123',\n    queryTypes: 'ALL'  // or: IMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT\n  })\n});\n\n// Profile summary (followers + aggregated stats)\nawait fetch('https://api.publora.com/api/v1/linkedin-profile-summary', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({ platformId: 'linkedin-ABC123' })\n});\n\nAvailable analytics endpoints:\n\nEndpointDescriptionPOST /linkedin-post-statisticsImpressions, reactions, reshares for a postPOST /linkedin-account-statisticsAggregated account metricsPOST /linkedin-followersFollower count and growthPOST /linkedin-profile-summaryCombined profile overviewPOST /linkedin-create-reactionReact to a postDELETE /linkedin-delete-reactionRemove a reaction"
      },
      {
        "title": "Webhooks",
        "body": "Get real-time notifications when posts are published, fail, or tokens are expiring.\n\n// Create a webhook\nawait fetch('https://api.publora.com/api/v1/webhooks', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    name: 'My webhook',\n    url: 'https://myapp.com/webhooks/publora',\n    events: ['post.published', 'post.failed', 'token.expiring']\n  })\n});\n// Returns: { webhook: { _id, name, url, events, secret, isActive } }\n// Save the `secret` — it's only shown once. Use it to verify webhook signatures.\n\nValid events: post.scheduled, post.published, post.failed, token.expiring\n\nEndpointMethodDescription/webhooksGETList all webhooks/webhooksPOSTCreate webhook/webhooks/:idPATCHUpdate webhook/webhooks/:idDELETEDelete webhook/webhooks/:id/regenerate-secretPOSTRotate webhook secret\n\nMax 10 webhooks per account."
      },
      {
        "title": "Workspace / B2B API",
        "body": "Manage multiple users under your workspace account. Contact serge@publora.com to enable Workspace API access.\n\n// Create a managed user\nconst user = await fetch('https://api.publora.com/api/v1/workspace/users', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_CORP_KEY' },\n  body: JSON.stringify({ email: 'client@example.com', displayName: 'Acme Corp' })\n}).then(r => r.json());\n\n// Generate connection URL for user to connect their social accounts\nconst { connectionUrl } = await fetch(\n  `https://api.publora.com/api/v1/workspace/users/${user.id}/connection-url`,\n  { method: 'POST', headers: { 'x-publora-key': 'sk_CORP_KEY' } }\n).then(r => r.json());\n\n// Post on behalf of managed user\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'x-publora-key': 'sk_CORP_KEY',\n    'x-publora-user-id': user.id  // ← key header for acting on behalf of a user\n  },\n  body: JSON.stringify({ content: 'Post for Acme Corp!', platforms: ['linkedin-XYZ'] })\n});\n\nWorkspace endpoints:\n\nEndpointMethodDescription/workspace/usersGETList managed users/workspace/usersPOSTCreate managed user/workspace/users/:userIdDELETERemove managed user/workspace/users/:userId/api-keyPOSTGenerate per-user API key/workspace/users/:userId/connection-urlPOSTGenerate OAuth connection link\n\nEach managed user has a limit of 100 posts/day (dailyPostsLeft). Never expose your workspace key client-side — use per-user API keys for client-facing scenarios."
      },
      {
        "title": "Platform Limits Quick Reference (API)",
        "body": "⚠️ API limits are often stricter than native app limits. Always design against these.\n\nPlatformChar LimitMax ImagesVideo MaxText Only?Twitter/X280 (25K Premium)4 × 5MB2 min / 512MB✅LinkedIn3,00020 × 5MB30 min / 500MB✅Instagram2,20010 × 8MB (JPEG only)90s / 300MB❌Threads50020 × 8MB5 min / 500MB✅TikTok2,200Video only10 min / 4GB❌YouTube5,000 descVideo only12h / 256GB❌Facebook63,20610 × 10MB45 min / 2GB✅Bluesky3004 × 1MB3 min / 100MB✅Mastodon5004 × 16MB~99MB✅Telegram4,096 (1,024 captions)10 × 10MB50MB (Bot API)✅\n\nFor full limits detail, see the docs/guides/platform-limits.md in the Publora API Docs."
      },
      {
        "title": "Platform-Specific Skills",
        "body": "For platform-specific settings, limits, and examples:\n\npublora-linkedin — LinkedIn posts + analytics + reactions\npublora-twitter — X/Twitter posts & threads\npublora-instagram — Instagram images/reels/carousels\npublora-threads — Threads posts\npublora-tiktok — TikTok videos\npublora-youtube — YouTube videos\npublora-facebook — Facebook page posts\npublora-bluesky — Bluesky posts\npublora-mastodon — Mastodon posts\npublora-telegram — Telegram channels"
      },
      {
        "title": "Post Statuses",
        "body": "draft — Not scheduled yet\nscheduled — Waiting to publish\npublished — Successfully posted\nfailed — Publishing failed (check /post-logs)\npartially_published — Some platforms failed"
      },
      {
        "title": "Errors",
        "body": "CodeMeaning400Invalid request (check scheduledTime format, required fields)401Invalid or missing API key403Plan limit reached or Workspace API not enabled404Post/resource not found429Platform rate limit exceeded"
      }
    ],
    "body": "Publora API — Core Skill\n\nPublora is an affordable REST API for scheduling and publishing social media posts across 11 platforms. Base URL: https://api.publora.com/api/v1\n\nAuthentication\n\nAll requests require the x-publora-key header. Keys start with sk_.\n\ncurl https://api.publora.com/api/v1/platform-connections \\\n  -H \"x-publora-key: sk_YOUR_KEY\"\n\n\nGet your key: publora.com → Settings → API Keys → Generate API Key. ⚠️ Copy immediately — shown only once.\n\nStep 0: Get Platform IDs\n\nAlways call this first to get valid platform IDs before posting.\n\nconst res = await fetch('https://api.publora.com/api/v1/platform-connections', {\n  headers: { 'x-publora-key': 'sk_YOUR_KEY' }\n});\nconst { connections } = await res.json();\n// connections[i].platformId → e.g. \"linkedin-ABC123\", \"twitter-456\"\n// Also returns: tokenStatus, tokenExpiresIn, lastSuccessfulPost, lastError\n\n\nPlatform IDs look like: twitter-123, linkedin-ABC, instagram-456, threads-789, etc.\n\nPost Immediately\n\nOmit scheduledTime to publish right away:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'Your post content here',\n    platforms: ['twitter-123', 'linkedin-ABC']\n  })\n});\n\nSchedule a Post\n\nInclude scheduledTime in ISO 8601 UTC — must be in the future:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'Scheduled post content',\n    platforms: ['twitter-123', 'linkedin-ABC'],\n    scheduledTime: '2026-03-16T10:00:00.000Z'\n  })\n});\n// Response: { postGroupId: \"pg_abc123\", scheduledTime: \"...\" }\n\nSave as Draft\n\nOmit scheduledTime — post is created as draft. Schedule it later:\n\n// Create draft\nconst { postGroupId } = await createPost({ content, platforms });\n\n// Schedule later\nawait fetch(`https://api.publora.com/api/v1/update-post/${postGroupId}`, {\n  method: 'PUT',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({ status: 'scheduled', scheduledTime: '2026-03-16T10:00:00.000Z' })\n});\n\nList Posts\n\nFilter, paginate and sort your scheduled/published posts:\n\n// GET /api/v1/list-posts\n// Query params: status, platform, fromDate, toDate, page, limit, sortBy, sortOrder\nconst res = await fetch(\n  'https://api.publora.com/api/v1/list-posts?status=scheduled&platform=twitter&page=1&limit=20',\n  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\nconst { posts, pagination } = await res.json();\n// pagination: { page, limit, totalItems, totalPages, hasNextPage, hasPrevPage }\n\n\nValid statuses: draft, scheduled, published, failed, partially_published\n\nGet / Delete a Post\n# Get post details\nGET /api/v1/get-post/:postGroupId\n\n# Delete post (also removes media from storage)\nDELETE /api/v1/delete-post/:postGroupId\n\nGet Post Logs\n\nDebug failed or partially published posts:\n\nconst res = await fetch(\n  `https://api.publora.com/api/v1/post-logs/${postGroupId}`,\n  { headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\nconst { logs } = await res.json();\n\nTest a Connection\n\nVerify a platform connection is healthy before posting:\n\nconst res = await fetch(\n  'https://api.publora.com/api/v1/test-connection/linkedin-ABC123',\n  { method: 'POST', headers: { 'x-publora-key': 'sk_YOUR_KEY' } }\n);\n// Returns: { status: \"ok\"|\"error\", message, permissions, tokenExpiresIn }\n\nBulk Schedule (a Week of Content)\nfrom datetime import datetime, timedelta, timezone\nimport requests\n\nHEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }\nbase_date = datetime(2026, 3, 16, 10, 0, 0, tzinfo=timezone.utc)\n\nposts = ['Monday post', 'Tuesday post', 'Wednesday post', 'Thursday post', 'Friday post']\n\nfor i, content in enumerate(posts):\n    scheduled_time = base_date + timedelta(days=i)\n    requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={\n        'content': content,\n        'platforms': ['twitter-123', 'linkedin-ABC'],\n        'scheduledTime': scheduled_time.isoformat()\n    })\n\nMedia Uploads\n\nAll media (images and videos) use a 3-step pre-signed upload workflow:\n\nStep 1: POST /api/v1/create-post → get postGroupId\nStep 2: POST /api/v1/get-upload-url → get uploadUrl\nStep 3: PUT {uploadUrl} with file bytes (no auth needed for S3)\n\nimport requests\n\nHEADERS = { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' }\n\n# Step 1: Create post\npost = requests.post('https://api.publora.com/api/v1/create-post', headers=HEADERS, json={\n    'content': 'Check this out!',\n    'platforms': ['instagram-456'],\n    'scheduledTime': '2026-03-15T14:30:00.000Z'\n}).json()\npost_group_id = post['postGroupId']\n\n# Step 2: Get pre-signed upload URL\nupload = requests.post('https://api.publora.com/api/v1/get-upload-url', headers=HEADERS, json={\n    'fileName': 'photo.jpg',\n    'contentType': 'image/jpeg',\n    'type': 'image',  # or 'video'\n    'postGroupId': post_group_id\n}).json()\n\n# Step 3: Upload directly to S3 (no auth header needed)\nwith open('./photo.jpg', 'rb') as f:\n    requests.put(upload['uploadUrl'], headers={'Content-Type': 'image/jpeg'}, data=f)\n\n\nFor carousels: call get-upload-url N times with the same postGroupId.\n\nCross-Platform Threading\n\nX/Twitter and Threads support auto-threading. Separate segments with --- on its own line:\n\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    content: 'First tweet in the thread.\\n\\n---\\n\\nSecond tweet continues.\\n\\n---\\n\\nFinal tweet wraps up.',\n    platforms: ['twitter-123', 'threads-789']\n  })\n});\n\n\n⚠️ Threads Restriction: Multi-threaded nested posts (content auto-split into connected replies) are temporarily unavailable on Threads due to Threads app reconnection status. Single posts and carousels continue to work normally. Contact support@publora.com for updates.\n\nLinkedIn Analytics\n// Post statistics\nawait fetch('https://api.publora.com/api/v1/linkedin-post-statistics', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    postedId: 'urn:li:share:7123456789',\n    platformId: 'linkedin-ABC123',\n    queryTypes: 'ALL'  // or: IMPRESSION, MEMBERS_REACHED, RESHARE, REACTION, COMMENT\n  })\n});\n\n// Profile summary (followers + aggregated stats)\nawait fetch('https://api.publora.com/api/v1/linkedin-profile-summary', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({ platformId: 'linkedin-ABC123' })\n});\n\n\nAvailable analytics endpoints:\n\nEndpoint\tDescription\nPOST /linkedin-post-statistics\tImpressions, reactions, reshares for a post\nPOST /linkedin-account-statistics\tAggregated account metrics\nPOST /linkedin-followers\tFollower count and growth\nPOST /linkedin-profile-summary\tCombined profile overview\nPOST /linkedin-create-reaction\tReact to a post\nDELETE /linkedin-delete-reaction\tRemove a reaction\nWebhooks\n\nGet real-time notifications when posts are published, fail, or tokens are expiring.\n\n// Create a webhook\nawait fetch('https://api.publora.com/api/v1/webhooks', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_YOUR_KEY' },\n  body: JSON.stringify({\n    name: 'My webhook',\n    url: 'https://myapp.com/webhooks/publora',\n    events: ['post.published', 'post.failed', 'token.expiring']\n  })\n});\n// Returns: { webhook: { _id, name, url, events, secret, isActive } }\n// Save the `secret` — it's only shown once. Use it to verify webhook signatures.\n\n\nValid events: post.scheduled, post.published, post.failed, token.expiring\n\nEndpoint\tMethod\tDescription\n/webhooks\tGET\tList all webhooks\n/webhooks\tPOST\tCreate webhook\n/webhooks/:id\tPATCH\tUpdate webhook\n/webhooks/:id\tDELETE\tDelete webhook\n/webhooks/:id/regenerate-secret\tPOST\tRotate webhook secret\n\nMax 10 webhooks per account.\n\nWorkspace / B2B API\n\nManage multiple users under your workspace account. Contact serge@publora.com to enable Workspace API access.\n\n// Create a managed user\nconst user = await fetch('https://api.publora.com/api/v1/workspace/users', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json', 'x-publora-key': 'sk_CORP_KEY' },\n  body: JSON.stringify({ email: 'client@example.com', displayName: 'Acme Corp' })\n}).then(r => r.json());\n\n// Generate connection URL for user to connect their social accounts\nconst { connectionUrl } = await fetch(\n  `https://api.publora.com/api/v1/workspace/users/${user.id}/connection-url`,\n  { method: 'POST', headers: { 'x-publora-key': 'sk_CORP_KEY' } }\n).then(r => r.json());\n\n// Post on behalf of managed user\nawait fetch('https://api.publora.com/api/v1/create-post', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n    'x-publora-key': 'sk_CORP_KEY',\n    'x-publora-user-id': user.id  // ← key header for acting on behalf of a user\n  },\n  body: JSON.stringify({ content: 'Post for Acme Corp!', platforms: ['linkedin-XYZ'] })\n});\n\n\nWorkspace endpoints:\n\nEndpoint\tMethod\tDescription\n/workspace/users\tGET\tList managed users\n/workspace/users\tPOST\tCreate managed user\n/workspace/users/:userId\tDELETE\tRemove managed user\n/workspace/users/:userId/api-key\tPOST\tGenerate per-user API key\n/workspace/users/:userId/connection-url\tPOST\tGenerate OAuth connection link\n\nEach managed user has a limit of 100 posts/day (dailyPostsLeft). Never expose your workspace key client-side — use per-user API keys for client-facing scenarios.\n\nPlatform Limits Quick Reference (API)\n\n⚠️ API limits are often stricter than native app limits. Always design against these.\n\nPlatform\tChar Limit\tMax Images\tVideo Max\tText Only?\nTwitter/X\t280 (25K Premium)\t4 × 5MB\t2 min / 512MB\t✅\nLinkedIn\t3,000\t20 × 5MB\t30 min / 500MB\t✅\nInstagram\t2,200\t10 × 8MB (JPEG only)\t90s / 300MB\t❌\nThreads\t500\t20 × 8MB\t5 min / 500MB\t✅\nTikTok\t2,200\tVideo only\t10 min / 4GB\t❌\nYouTube\t5,000 desc\tVideo only\t12h / 256GB\t❌\nFacebook\t63,206\t10 × 10MB\t45 min / 2GB\t✅\nBluesky\t300\t4 × 1MB\t3 min / 100MB\t✅\nMastodon\t500\t4 × 16MB\t~99MB\t✅\nTelegram\t4,096 (1,024 captions)\t10 × 10MB\t50MB (Bot API)\t✅\n\nFor full limits detail, see the docs/guides/platform-limits.md in the Publora API Docs.\n\nPlatform-Specific Skills\n\nFor platform-specific settings, limits, and examples:\n\npublora-linkedin — LinkedIn posts + analytics + reactions\npublora-twitter — X/Twitter posts & threads\npublora-instagram — Instagram images/reels/carousels\npublora-threads — Threads posts\npublora-tiktok — TikTok videos\npublora-youtube — YouTube videos\npublora-facebook — Facebook page posts\npublora-bluesky — Bluesky posts\npublora-mastodon — Mastodon posts\npublora-telegram — Telegram channels\nPost Statuses\ndraft — Not scheduled yet\nscheduled — Waiting to publish\npublished — Successfully posted\nfailed — Publishing failed (check /post-logs)\npartially_published — Some platforms failed\nErrors\nCode\tMeaning\n400\tInvalid request (check scheduledTime format, required fields)\n401\tInvalid or missing API key\n403\tPlan limit reached or Workspace API not enabled\n404\tPost/resource not found\n429\tPlatform rate limit exceeded"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/sergebulaev/publora",
    "publisherUrl": "https://clawhub.ai/sergebulaev/publora",
    "owner": "sergebulaev",
    "version": "1.2.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/publora",
    "downloadUrl": "https://openagent3.xyz/downloads/publora",
    "agentUrl": "https://openagent3.xyz/skills/publora/agent",
    "manifestUrl": "https://openagent3.xyz/skills/publora/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/publora/agent.md"
  }
}