openapi: 3.1.0 info: title: Planable Public Campaigns Pages API version: 1.0.0 description: REST API for managing Planable companies, workspaces, pages, posts and more. servers: - url: https://api.planable.io/api/v1 security: - bearerAuth: [] tags: - name: Pages paths: /pages/{id}/sync: post: tags: - Pages summary: Trigger page metrics sync description: Triggers a fresh analytics metrics sync for a page. security: - bearerAuth: - write parameters: - name: id in: path required: true schema: type: string responses: '200': description: Page metrics sync queued or deduplicated content: application/json: schema: type: object properties: data: type: object properties: pageId: type: string started: type: boolean message: type: string required: - pageId - started - message required: - data '400': description: Page type does not support analytics sync '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden '404': description: Page not found '429': description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /pages/{id}/sync-status: get: tags: - Pages summary: Get page metrics sync status description: Returns whether a page metrics sync is in progress and when metrics last synced. parameters: - name: id in: path required: true schema: type: string security: - bearerAuth: [] responses: '200': description: Page metrics sync status content: application/json: schema: type: object properties: data: type: object properties: pageId: type: string syncInProgress: type: boolean lastSyncedAt: type: - string - 'null' format: date-time required: - pageId - syncInProgress - lastSyncedAt required: - data '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden '404': description: Page not found '429': description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /pages: get: tags: - Pages summary: List pages description: Returns pages (social channels) the token has access to, with pagination. parameters: - name: workspaceId in: query required: false schema: type: string - name: offset in: query required: false schema: type: integer default: 0 - name: limit in: query required: false schema: type: integer default: 20 security: - bearerAuth: [] responses: '200': description: List of pages with pagination content: application/json: schema: type: object properties: data: type: array items: type: object properties: id: type: string name: type: string platform: type: string workspaceId: type: string createdAt: type: - string - 'null' format: date-time isConnected: type: boolean profilePicture: type: - string - 'null' link: type: - string - 'null' required: - id - name - platform - workspaceId - createdAt - isConnected - profilePicture - link pagination: type: object properties: offset: type: number limit: type: number hasMore: type: boolean required: - offset - limit - hasMore required: - data - pagination '400': description: Validation error '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '429': description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' /pages/{id}/metrics: get: tags: - Pages summary: Get page metrics description: Returns paginated raw page metrics snapshots for a date range. parameters: - name: id in: path required: true schema: type: string - name: startDate in: query required: true schema: type: string format: date-time - name: endDate in: query required: true schema: type: string format: date-time - name: offset in: query required: false schema: type: integer minimum: 0 default: 0 - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 90 default: 30 security: - bearerAuth: [] responses: '200': description: Paginated page metrics snapshots content: application/json: schema: type: object properties: data: type: array items: type: object properties: pageId: type: string pageType: type: string fetchedAt: type: string format: date-time partial: type: boolean facebook: type: object properties: followers: type: number gainedFollowers: type: number posts: type: number engagement: type: number reactions: type: number pageViews: type: number unFollows: type: number views: type: number totalFollowers: type: number required: - followers - gainedFollowers - posts - engagement - reactions - pageViews - unFollows - views instagram: type: object properties: gainedFollowers: type: number unFollows: type: number totalFollowers: type: number followersIncomplete: type: boolean impressions: type: number views: type: number reach: type: number accountsReachBreakdown: type: object properties: followers: type: number nonFollowers: type: number total: type: number required: - followers - nonFollowers - total profileViews: type: number posts: type: number engagement: type: number engagementBreakdown: type: object properties: post: type: number reel: type: number ad: type: number story: type: number carousel: type: number reachBreakdown: type: object properties: post: type: object properties: total: type: number followers: type: number nonFollowers: type: number required: - total - followers - nonFollowers reel: type: object properties: total: type: number followers: type: number nonFollowers: type: number required: - total - followers - nonFollowers story: type: object properties: total: type: number followers: type: number nonFollowers: type: number required: - total - followers - nonFollowers required: - post - reel - story required: - gainedFollowers - unFollows - reach - posts - engagement linkedin: type: object properties: followers: type: number totalFollowers: type: number impressions: type: number engagement: type: number pageInsights: type: number pageViews: type: number posts: type: number reshares: type: number reactions: type: number comments: type: number shareStats: type: object properties: uniqueImpressionsCount: type: number clickCount: type: number engagement: type: number likeCount: type: number commentCount: type: number shareCount: type: number impressionCount: type: number commentMentionsCount: type: number shareMentionsCount: type: number required: - uniqueImpressionsCount - clickCount - engagement - likeCount - commentCount - shareCount - impressionCount required: - followers - impressions tiktok: type: object properties: profile_views: type: number video_views: type: number likes: type: number comments: type: number shares: type: number followers_count: type: number engagement: type: number videos: type: number required: - profile_views - video_views - likes - comments - shares - followers_count - engagement - videos threads: type: object properties: views: type: number likes: type: number replies: type: number reposts: type: number quotes: type: number required: - views - likes - replies - reposts - quotes googleMyBusiness: type: object properties: posts: type: number callClicks: type: number websiteClicks: type: number impressions: type: number engagement: type: number businessConversations: type: number businessImpressionsMobileSearch: type: number businessImpressionsDesktopMaps: type: number businessImpressionsDesktopSearch: type: number businessImpressionsMobileMaps: type: number businessDirectionRequests: type: number businessBookings: type: number businessFoodOrders: type: number required: - posts - callClicks - websiteClicks - impressions - engagement - businessConversations - businessImpressionsMobileSearch - businessImpressionsDesktopMaps - businessImpressionsDesktopSearch - businessImpressionsMobileMaps - businessDirectionRequests - businessBookings - businessFoodOrders youtube: type: object properties: subscribers: type: number totalSubscribers: type: number subscribersGained: type: number subscribersLost: type: number videos: type: number views: type: number estimatedMinutesWatched: type: number comments: type: number likes: type: number dislikes: type: number shares: type: number engagement: type: number averageViewDuration: type: number required: - subscribers - subscribersGained - subscribersLost - videos - views - estimatedMinutesWatched - comments - likes - dislikes - shares - engagement - averageViewDuration required: - pageId - pageType - fetchedAt - partial pagination: type: object properties: offset: type: number limit: type: number hasMore: type: boolean required: - offset - limit - hasMore required: - data - pagination '400': description: Validation error '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '403': description: Forbidden '404': description: Page not found '429': description: Rate limit exceeded content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' components: schemas: ErrorResponse: type: object properties: error: type: object properties: code: type: string enum: - VALIDATION_ERROR - UNAUTHORIZED - FORBIDDEN - NOT_FOUND - METHOD_NOT_ALLOWED - RATE_LIMITED - POST_ALREADY_PUBLISHED - PUBLISHING_IN_PROGRESS - WORKSPACE_LIMIT_REACHED - INTERNAL message: type: string requestId: type: string required: - code - message - requestId required: - error securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: pln_*