Test Connection

View as Markdown

Validate that a platform connection is working before scheduling posts.

Endpoint

POST https://api.publora.com/api/v1/test-connection/:platformId

Headers

Header Required Description
x-publora-key Yes Your API key
x-publora-user-id No Managed user ID (workspace only)
x-publora-client No Client identifier (e.g., mcp). Used for MCP access gating.

Path Parameters

Parameter Type Description
platformId string The platform ID (e.g., linkedin-Tz9W5i6ZYG)

Response

Successful Connection

{
  "platform": "linkedin",
  "platformId": "linkedin-Tz9W5i6ZYG",
  "username": "John Doe",
  "status": "ok",
  "message": "Connected as John Doe",
  "permissions": ["w_member_social", "w_member_social_feed", "r_basicprofile", "r_member_postAnalytics", "r_member_profileAnalytics"],
  "tokenExpiresIn": "45d 3h",
  "lastSuccessfulPost": "2026-02-22T09:15:00.000Z",
  "lastError": null
}

Failed Connection

{
  "platform": "threads",
  "platformId": "threads-17841412345678",
  "username": "yourthreads",
  "status": "error",
  "message": "Error validating access token: Session has expired",
  "permissions": [],
  "tokenExpiresIn": "expired",
  "lastSuccessfulPost": "2026-02-15T14:30:00.000Z",
  "lastError": {
    "message": "Session has expired",
    "occurredAt": "2026-02-22T10:00:00.000Z"
  }
}

Important: All validation results — both success and failure — return HTTP 200. The connection test outcome is indicated by the status field ("ok" or "error"), not the HTTP status code. Only infrastructure errors (404 connection not found, 500 server error) use non-200 status codes.

Response Fields

Field Type Description
platform string Platform name (twitter, linkedin, threads, etc.)
platformId string Full platform ID
username string/null Connected account username. null for a Telegram channel without a public username (a private channel).
status string ok or error
message string Human-readable status message
permissions array List of granted permissions/scopes. With the YouTube update in #490, these are Google's recorded full scope URIs; an unknown grant returns [].
permissionsKnown boolean YouTube only, added in #490. Whether the granted scopes are recorded. false means unknown, not permission denied.
tokenExpiresIn string/null Time until token expiry. Possible values: "7d 3h" (days and hours), "5h" (hours only, when less than one day remains), "< 1h" (less than one hour remaining), "expired" (token already expired), or null (no expiration).
lastSuccessfulPost string/null Timestamp of last successful post
lastError object/null Last error details if any. When present, contains: message (string — error description) and occurredAt (string — ISO 8601 timestamp of when the error occurred).

YouTube permissions — #490 update

Publora #490 corrects the previous fixed ["youtube.upload", "youtube.readonly"] list. Since 2026-10-01, YouTube responses return the actual scopes recorded from Google's OAuth response, using their full URIs. For a canonical broad grant, the relevant response fields are:

{
  "status": "ok",
  "permissions": ["https://www.googleapis.com/auth/youtube"],
  "permissionsKnown": true
}

A broad youtube grant covers uploading videos and adding them to a playlist; it is not expanded into separately granted youtube.upload or youtube.readonly entries. See Google's scope definitions and playlist-write authorization.

Older connections without a recorded grant, or a reconnect where Google omits scopes, return permissions: [] and permissionsKnown: false. Do not interpret that empty array as proof that permission was denied. If your integration gates operations by scopes, handle unknown grants separately and recognize broad youtube access; checking for the old short youtube.upload string is insufficient. Use status for the connection health result. Other platforms' responses keep their current format.

Examples

JavaScript (fetch)

const platformId = 'linkedin-Tz9W5i6ZYG';
const response = await fetch(
  `https://api.publora.com/api/v1/test-connection/${platformId}`,
  {
    method: 'POST',
    headers: { 'x-publora-key': 'YOUR_API_KEY' }
  }
);
const result = await response.json();
 
if (result.status === 'ok') {
  console.log(`✅ ${result.message}`);
  console.log(`Token expires in: ${result.tokenExpiresIn}`);
} else {
  console.log(`❌ Connection failed: ${result.message}`);
}

Python (requests)

import requests
 
platform_id = 'linkedin-Tz9W5i6ZYG'
response = requests.post(
    f'https://api.publora.com/api/v1/test-connection/{platform_id}',
    headers={'x-publora-key': 'YOUR_API_KEY'}
)
result = response.json()
 
if result['status'] == 'ok':
    print(f"✅ {result['message']}")
    print(f"Permissions: {', '.join(result['permissions'])}")
else:
    print(f"❌ {result['message']}")

cURL

curl -X POST https://api.publora.com/api/v1/test-connection/linkedin-Tz9W5i6ZYG \
  -H "x-publora-key: YOUR_API_KEY"

Pre-flight Check Before Scheduling

async function validateConnectionsBeforePost(platformIds) {
  const results = await Promise.all(
    platformIds.map(async (platformId) => {
      const response = await fetch(
        `https://api.publora.com/api/v1/test-connection/${platformId}`,
        {
          method: 'POST',
          headers: { 'x-publora-key': process.env.PUBLORA_API_KEY }
        }
      );
      return response.json();
    })
  );
 
  const failed = results.filter(r => r.status === 'error');
  if (failed.length > 0) {
    console.log('⚠️  Some connections need attention:');
    failed.forEach(r => console.log(`  - ${r.platformId}: ${r.message}`));
    return false;
  }
 
  console.log('✅ All connections healthy');
  return true;
}
 
// Usage
const platforms = ['twitter-123', 'linkedin-ABC', 'threads-456'];
const healthy = await validateConnectionsBeforePost(platforms);
if (healthy) {
  // Proceed with scheduling
}

Telegram Connections

For a Telegram bot connection, the check verifies the bot token, that the bot can still read the saved channel, and that the bot is an administrator allowed to post there. A private channel without a public username returns "username": null.

status message Meaning
ok Bot can post to <channel title> The token works and the bot is an administrator with posting rights
error Bot needs channel administrator posting rights The bot is no longer an administrator, or lost the Post Messages right in a channel
error Channel is unavailable The saved chat is not a channel or supergroup
error Invalid bot token Telegram answered without confirming the bot. A revoked token normally fails with Request failed with status code 401 instead (see below)
error Invalid bot connection Not a usable bot connection (no bot token or chat ID). Every MTProto connection returns this

Errors returned by the Telegram API itself, such as a revoked bot token or a private channel the bot was removed from, come back as Request failed with status code <code>.

Note: Telegram MTProto connections will always fail the test-connection check because the validator only checks bot connections. They return "Invalid bot connection", but still publish normally.

Unknown Platform

If the platform has no test-connection validator implemented (e.g., Pinterest), the endpoint returns HTTP 200 with a full response structure but error status:

{
  "platform": "pinterest",
  "platformId": "pinterest-abc123",
  "username": "unknown",
  "status": "error",
  "message": "Unknown platform: pinterest",
  "permissions": [],
  "tokenExpiresIn": null,
  "lastSuccessfulPost": null,
  "lastError": null
}

Note: The public API route returns only { error: "Failed to test connection" } for 500 errors without a message field. The internal dashboard endpoint may include an additional message field in error responses.

Errors

Status Error Cause
400 "Invalid platformId format" platformId doesn't contain a dash or is malformed. Note: Parsed the same way as in create-post: split at the first dash (so a Telegram bot ID such as telegram--1001234567890 is valid), with a lowercase-letter prefix; the non-empty ID may not contain whitespace, /, ?, or #; colons are allowed.
400 "Invalid x-publora-user-id" x-publora-user-id header contains an invalid ObjectId
401 "API key is required" Missing x-publora-key header
401 "Invalid API key" x-publora-key value is not a valid key
401 "Invalid API key owner" The API key's owner account could not be found
403 "API access is not enabled for this account" Account does not have API access enabled
403 "MCP access is not enabled for this account" MCP access is not enabled when x-publora-client: mcp is sent
403 "Workspace access is not enabled for this key" API key does not have workspace permissions
403 "User is not managed by key" The x-publora-user-id user is not managed by this API key
500 "Internal server error" Unexpected error in middleware
404 "Connection not found" Invalid platformId or connection doesn't exist
500 "Failed to test connection" Server error during validation

Publora is built by Creative Content Crafts, Inc. Need AI-powered content creation for LinkedIn, Threads, and X? Try Co.Actor — the best AI service for authentic thought leadership at scale.