{"openapi":"3.0.0","paths":{"/v1/public/account":{"get":{"description":"Business profile, plan, credit balance, channel summary, and autopilot state.","operationId":"PublicAccountController_getAccount_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAccountDto"}}}}},"security":[{"bearer":[]}],"summary":"Account overview","tags":["Public API - Account"]}},"/v1/public/account/credits":{"get":{"operationId":"PublicAccountController_getCredits_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCreditsDto"}}}}},"security":[{"bearer":[]}],"summary":"Credit balance","tags":["Public API - Account"]}},"/v1/public/analytics/performance":{"get":{"description":"Live engagement for recent published posts. Instagram only today; unavailability is stated plainly with a machine reason, never fabricated.","operationId":"PublicAnalyticsController_getPerformance_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPerformanceResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Recent performance","tags":["Public API - Analytics"]}},"/v1/public/autopilot":{"get":{"operationId":"PublicAutopilotController_getAutopilot_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAutopilotResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Autopilot plans","tags":["Public API - Autopilot"]}},"/v1/public/autopilot/queue":{"get":{"description":"The rolling 7-day schedule with day-level coverage. This read never triggers generation.","operationId":"PublicAutopilotController_getQueue_v1","parameters":[{"name":"from","required":false,"in":"query","description":"Window start (ISO); defaults to now","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicQueueResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Weekly queue","tags":["Public API - Autopilot"]}},"/v1/public/brand":{"patch":{"description":"Structured fields only (description, industry, audience, tone, colors, keywords). Channel handles and internal fields stay in the app.","operationId":"PublicBrandController_updateBrand_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePublicBrandDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBrandDto"}}}}},"security":[{"bearer":[]}],"summary":"Update the brand kit","tags":["Public API - Brand"]},"get":{"description":"Colors, fonts, tone, keywords, logo, and the brand image library. Image URLs are signed and expire after ~6 hours.","operationId":"PublicBrandController_getBrand_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBrandDto"}}}}},"security":[{"bearer":[]}],"summary":"Brand kit","tags":["Public API - Brand"]}},"/v1/public/brand/logo":{"put":{"operationId":"PublicBrandController_setLogo_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetBrandLogoDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBrandDto"}}}}},"security":[{"bearer":[]}],"summary":"Set the brand logo from an uploaded attachment","tags":["Public API - Brand"]}},"/v1/public/brand/memories":{"get":{"description":"Durable facts and timely notes that steer generated content.","operationId":"PublicBrandController_listMemories_v1","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Brand memories","tags":["Public API - Brand"]},"post":{"operationId":"PublicBrandController_addMemory_v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddBrandMemoryDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBrandMemoryDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a brand memory (a durable fact)","tags":["Public API - Brand"]}},"/v1/public/brand/memories/{memoryId}":{"delete":{"operationId":"PublicBrandController_deleteMemory_v1","parameters":[{"name":"memoryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove a brand memory","tags":["Public API - Brand"]}},"/v1/public/brand/images":{"get":{"description":"Real brand photos with content tags. Use an attachmentId with posts/{id}/set-image. URLs are signed and expire (~6h).","operationId":"PublicBrandController_listBrandImages_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBrandImagesResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Brand image library","tags":["Public API - Brand"]}},"/v1/public/calendar":{"get":{"description":"Scheduled and recently published posts in a date window (max 92 days). Published posts appear for at most the trailing 30 days.","operationId":"PublicCalendarController_getCalendar_v1","parameters":[{"name":"from","required":true,"in":"query","description":"Window start (ISO date, business timezone)","schema":{"example":"2026-08-01","type":"string"}},{"name":"to","required":true,"in":"query","description":"Window end (ISO date, business timezone)","schema":{"example":"2026-08-31","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicCalendarResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Content calendar","tags":["Public API - Calendar"]}},"/v1/public/channels":{"get":{"description":"The business's social channel connections (what publishing actually checks). Connecting a channel requires a browser; see connectUrl.","operationId":"PublicChannelsController_listChannels_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicChannelsResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Connected channels","tags":["Public API - Channels"]}},"/v1/public/channels/{platform}/connect-link":{"post":{"description":"There is no headless channel connect: the platform OAuth flow needs the user in a browser. This returns the app page where they finish connecting.","operationId":"PublicChannelsController_connectLink_v1","parameters":[{"name":"platform","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectLinkResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a browser link to connect a channel","tags":["Public API - Channels"]}},"/v1/public/ideas":{"get":{"description":"Topics with their suggested posts. A fresh account has none (call generate first). Suggested posts are ephemeral suggestions, not stable resources.","operationId":"PublicIdeasController_listIdeas_v1","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicIdeasResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List ideas","tags":["Public API - Ideas"]}},"/v1/public/ideas/generate":{"post":{"description":"Topic seeds cost no credits. Poll the job; ideas land in GET /ideas.","operationId":"PublicIdeasController_generateIdeas_v1","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateIdeasDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobAcceptedDto"}}}}},"security":[{"bearer":[]}],"summary":"Generate idea topics (job)","tags":["Public API - Ideas"]}},"/v1/public/ideas/{ideaId}/pick":{"post":{"description":"Marks the idea picked and, if it has fewer than 3 suggested posts, spawns up to 3 in the background (this draws credits). Poll GET /ideas to watch them land, then promote one.","operationId":"PublicIdeasController_pickIdea_v1","parameters":[{"name":"ideaId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicIdeaPickResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Pick an idea","tags":["Public API - Ideas"]}},"/v1/public/ideas/{ideaId}/dismiss":{"post":{"operationId":"PublicIdeasController_dismissIdea_v1","parameters":[{"name":"ideaId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Dismiss an idea","tags":["Public API - Ideas"]}},"/v1/public/ideas/posts/{postId}/promote":{"post":{"description":"The post becomes a normal draft, visible in GET /posts.","operationId":"PublicIdeasController_promoteIdeaPost_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Promote a suggested post into a real draft","tags":["Public API - Ideas"]}},"/v1/public/jobs/{jobId}":{"get":{"description":"Long-running verbs return 202 with a job id; poll it here. A job that can't complete is failed with code 'timeout' by a reaper, so polling never hangs forever. Rows are kept for 30 days.","operationId":"PublicJobsController_getJob_v1","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobDto"}}}}},"security":[{"bearer":[]}],"summary":"Poll a job","tags":["Public API - Jobs"]}},"/v1/public/jobs":{"get":{"operationId":"PublicJobsController_listJobs_v1","parameters":[{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["queued","running","succeeded","failed"]}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":25,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobsListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List recent jobs","tags":["Public API - Jobs"]}},"/v1/public/media":{"post":{"description":"multipart/form-data with a `file` field. Images (JPEG/PNG/WebP/HEIC) up to 50MB, video (MP4/MOV) up to 150MB. iPhone HEIC photos are converted to JPEG. Use the returned id with posts/from-media or set-image.","operationId":"PublicMediaController_upload_v1","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary"}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicMediaDto"}}}}},"security":[{"bearer":[]}],"summary":"Upload media","tags":["Public API - Media"]}},"/v1/public/media/{mediaId}":{"get":{"description":"The URL is signed and expires (~6h); re-fetch rather than caching it.","operationId":"PublicMediaController_getMedia_v1","parameters":[{"name":"mediaId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicMediaDto"}}}}},"security":[{"bearer":[]}],"summary":"Get media metadata and a fresh download URL","tags":["Public API - Media"]}},"/v1/public/posts":{"get":{"description":"Posts with their per-platform variants, newest first. Media URLs are signed and expire after ~6 hours; never cache them.","operationId":"PublicPostsController_listPosts_v1","parameters":[{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["draft","scheduled","published"]}},{"name":"platform","required":false,"in":"query","schema":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]}},{"name":"from","required":false,"in":"query","description":"Only posts created at/after this ISO timestamp","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"Only posts created before this ISO timestamp","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Cursor from a previous page","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"maximum":100,"default":20,"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostsListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List posts","tags":["Public API - Posts"]},"post":{"description":"Creates an empty draft on one platform. Call generate to fill it with AI content, or use posts/from-media for your own media.","operationId":"PublicPostsWriteController_createPost_v1","parameters":[{"name":"Idempotency-Key","in":"header","description":"Replay-safe retries: the same key returns the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a draft post","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}":{"get":{"description":"The full post with per-platform variants, media, and quality scores.","operationId":"PublicPostsController_getPost_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Get a post","tags":["Public API - Posts"]},"patch":{"description":"Direct edit (no credits). body maps to the field the platform publishes. Send null to clear a field; omitted fields are preserved. Editing the primary re-derives other platforms; editing a sibling pins it.","operationId":"PublicPostsWriteController_updateVariant_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePostVariantDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Edit a variant's text","tags":["Public API - Posts"]},"delete":{"description":"Deletes the whole post including every platform variant. Not reversible.","operationId":"PublicPostsWriteController_deletePost_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Delete a post","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/metrics":{"get":{"description":"Instagram only today, and only within roughly the 25 most recent posts of the account. Unavailability is stated plainly with a machine reason.","operationId":"PublicPostsController_getPostMetrics_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostMetricsResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Live metrics for a post","tags":["Public API - Posts"]}},"/v1/public/posts/from-media":{"post":{"description":"Your uploads are published untouched (1 video = reel, 1 image = post, 2-10 images = carousel). AI editing is disabled for these posts by design. Schedule it with the schedule verb.","operationId":"PublicPostsWriteController_createFromMedia_v1","parameters":[{"name":"Idempotency-Key","in":"header","description":"Replay-safe retries: the same key returns the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostFromMediaDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a post from your own media","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/name":{"patch":{"operationId":"PublicPostsWriteController_renamePost_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenamePostDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Rename a post","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/versions":{"get":{"operationId":"PublicPostsWriteController_listVersions_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"platform","required":false,"in":"query","description":"Defaults to the primary variant","schema":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicVersionsResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Version history of a variant","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/versions/{seq}/restore":{"post":{"description":"Non-destructive: the restored state comes back as a new tip version.","operationId":"PublicPostsWriteController_restoreVersion_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"seq","required":true,"in":"path","description":"Version number to restore","schema":{"type":"number"}},{"name":"platform","required":false,"in":"query","description":"Defaults to the primary variant","schema":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicVersionsResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Restore a version","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/set-image":{"post":{"description":"No credits. Not available on posts made from your own media.","operationId":"PublicPostsWriteController_setImage_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPostImageDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPostDto"}}}}},"security":[{"bearer":[]}],"summary":"Set the image from your media or brand library","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/generate":{"post":{"description":"Draws from your credit balance (charged on success only; static 1, carousel 2-10, reel 1). Returns 202 with a job; reels can take ~15 minutes.","operationId":"PublicPostsWriteController_generate_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Replay-safe retries: the same key returns the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePostBodyDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobAcceptedDto"}}}}},"security":[{"bearer":[]}],"summary":"Generate the post with AI (job)","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/revise":{"post":{"description":"Semantic revision: the engine keeps what works. The job result carries honesty flags (revisionApplied, noVisibleChange, imageryReverted). Narrate from them, never assume the change landed.","operationId":"PublicPostsWriteController_revise_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Replay-safe retries: the same key returns the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevisePostBodyDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobAcceptedDto"}}}}},"security":[{"bearer":[]}],"summary":"Revise the post with AI feedback (job)","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/regenerate-image":{"post":{"operationId":"PublicPostsWriteController_regenerateImage_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Replay-safe retries: the same key returns the original response for 24h.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateImageBodyDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobAcceptedDto"}}}}},"security":[{"bearer":[]}],"summary":"Regenerate the image, keep the copy (job)","tags":["Public API - Posts"]}},"/v1/public/posts/{postId}/schedule":{"post":{"description":"Schedules (or reschedules) a variant. Requires the channel to be connected and a future time. A schedule records the slot; check eligibleToPublish for whether it will actually publish (billing, approval, and plan gates can hold it).","operationId":"PublicPublishController_schedule_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulePostDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledVariantDto"}}}}},"security":[{"bearer":[]}],"summary":"Schedule a post","tags":["Public API - Scheduling & Publishing"]}},"/v1/public/posts/{postId}/unschedule":{"post":{"operationId":"PublicPublishController_unschedule_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantScopedDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduledVariantDto"}}}}},"security":[{"bearer":[]}],"summary":"Unschedule a post","tags":["Public API - Scheduling & Publishing"]}},"/v1/public/posts/{postId}/approve":{"post":{"description":"Stamps approval on the post so it publishes at its slot. CAUTION: approving a post whose slot already passed publishes it IMMEDIATELY (the missed-slot recovery); publishedImmediately says when that happened.","operationId":"PublicPublishController_approve_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantScopedDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveResultDto"}}}}},"security":[{"bearer":[]}],"summary":"Approve a review-mode post","tags":["Public API - Scheduling & Publishing"]}},"/v1/public/posts/{postId}/publish":{"post":{"description":"Publishes to the connected channel. A review-mode post must be approved first (409 APPROVAL_REQUIRED). Instagram/Facebook/LinkedIn/X return the result inline; TikTok/YouTube/Pinterest return 202 with a job to poll. success:false responses carry a plain-language error and whether retrying can help.","operationId":"PublicPublishController_publish_v1","parameters":[{"name":"postId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Strongly recommended: prevents accidental double publishes on retries.","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VariantScopedDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishResultDto"}}}},"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicJobAcceptedDto"}}}}},"security":[{"bearer":[]}],"summary":"Publish a post now","tags":["Public API - Scheduling & Publishing"]}}},"info":{"title":"Taka Public API","description":"Drive your own Taka account programmatically. A business Owner/Admin creates an API key (POST /v1/api-keys with an app session; an in-app settings page is on the way) and you send it as `Authorization: Bearer taka_live_...`. Errors use a stable {error:{code,message,requestId}} envelope; long-running verbs return 202 with a job to poll; media URLs expire after ~6 hours.","version":"1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT","type":"http"}},"schemas":{"PublicAccountPlanDto":{"type":"object","properties":{"tier":{"type":"string","description":"Plan tier","example":"pro"},"billingPeriod":{"type":"string","description":"Billing period of the active subscription"},"renewsAt":{"type":"string","description":"When the subscription renews (ISO)"},"hasActiveSubscription":{"type":"boolean","description":"Whether an active subscription exists (publishing requires one)"}},"required":["tier","hasActiveSubscription"]},"PublicCreditsDto":{"type":"object","properties":{"subscriptionCredits":{"type":"number","description":"Credits from the subscription plan (reset each cycle)"},"topUpCredits":{"type":"number","description":"Purchased or granted credits (never expire; spent after plan credits)"},"totalCredits":{"type":"number","description":"Total credits available"}},"required":["subscriptionCredits","topUpCredits","totalCredits"]},"PublicChannelDto":{"type":"object","properties":{"platform":{"type":"string","description":"Platform id","example":"instagram"},"displayName":{"type":"string","description":"Human-readable platform name"},"connected":{"type":"boolean","description":"Whether the business has this channel connected"},"account":{"type":"string","description":"Connected account handle or page name"},"available":{"type":"boolean","description":"Whether Taka supports this channel today (false = coming soon)"},"needsReconnect":{"type":"boolean","description":"The channel token needs the user to re-authorize in the app before publishing works"},"restricted":{"type":"boolean","description":"The platform itself is blocking publishing for this account (token is healthy; only the user can resolve it with the platform)"},"connectedAt":{"format":"date-time","type":"string","description":"When the channel was connected"}},"required":["platform","displayName","connected","available","needsReconnect","restricted"]},"PublicAccountDto":{"type":"object","properties":{"businessId":{"type":"string","description":"Business id"},"businessName":{"type":"string","description":"Business name"},"onboardingComplete":{"type":"boolean","description":"False while onboarding is incomplete"},"plan":{"$ref":"#/components/schemas/PublicAccountPlanDto"},"credits":{"nullable":true,"description":"null = the read failed, not zero credits","type":"object","allOf":[{"$ref":"#/components/schemas/PublicCreditsDto"}]},"channels":{"description":"Connected-channel summary","type":"array","items":{"$ref":"#/components/schemas/PublicChannelDto"}},"activeAutopilotPlans":{"type":"number","description":"Number of active autopilot plans"}},"required":["businessId","businessName","onboardingComplete","plan","credits","channels","activeAutopilotPlans"]},"PublicPerformancePostDto":{"type":"object","properties":{"platform":{"type":"string","description":"Platform the metrics come from","example":"instagram"},"permalink":{"type":"string","description":"Public permalink"},"publishedAt":{"type":"string","description":"ISO published time"},"caption":{"type":"string"},"likes":{"type":"number"},"comments":{"type":"number"}},"required":["platform","publishedAt","likes","comments"]},"PublicPerformanceResponseDto":{"type":"object","properties":{"available":{"type":"boolean","description":"False when metrics could not be served; see reason"},"reason":{"type":"string","description":"Why metrics are unavailable: analytics_not_enabled | channel_not_connected | channel_token_unavailable | metrics_unavailable"},"posts":{"type":"array","items":{"$ref":"#/components/schemas/PublicPerformancePostDto"}},"coverage":{"type":"string","description":"Honest coverage statement: live metrics exist for Instagram only, for roughly the 25 most recent posts. Other platforms publish fine but are not tracked yet."}},"required":["available","posts","coverage"]},"PublicAutopilotPlanDto":{"type":"object","properties":{"id":{"type":"string","description":"Plan id"},"channel":{"type":"string","description":"Primary channel of the plan"},"channels":{"description":"All channels the plan fans out to","type":"array","items":{"type":"string"}},"direction":{"type":"string","description":"Content direction","example":"expertise"},"status":{"type":"string","enum":["active","paused"]},"publishMode":{"type":"string","enum":["review","automatic"],"description":"New posts generated under review need approval before publishing. Snapshotted per post at generation time."},"preferredDays":{"description":"Weekdays the plan posts on (authoritative; its length = posts per week)","type":"array","items":{"type":"string"}},"preferredTime":{"type":"string","description":"Preferred posting time (HH:mm, business timezone)"}},"required":["id","channel","channels","direction","status","publishMode"]},"PublicAutopilotResponseDto":{"type":"object","properties":{"plans":{"type":"array","items":{"$ref":"#/components/schemas/PublicAutopilotPlanDto"}}},"required":["plans"]},"PublicQueueItemDto":{"type":"object","properties":{"variantId":{"type":"string","description":"Variant id (use with approve/schedule verbs)"},"postId":{"type":"string","description":"Post id"},"name":{"type":"string"},"platform":{"type":"string"},"status":{"type":"string","description":"Queue status: scheduled | needs_approval | generating | published | failed. 'needs_approval' posts publish only after approval."},"scheduledAt":{"type":"string"},"holdReason":{"type":"string","enum":["approval_missed","missed"],"description":"Why a past-due post has not published. approval_missed: approve it to publish immediately. missed: a server-side publish gate held it (check billingHold)."},"thumbnailUrl":{"type":"string","description":"Signed thumbnail (expires ~6h)"}},"required":["variantId","name","platform","status"]},"PublicQueueDayDto":{"type":"object","properties":{"date":{"type":"string","description":"Wall date in the business timezone (YYYY-MM-DD)"},"expectedCount":{"type":"number","description":"Posts the plan expects on this day"},"actualCount":{"type":"number","description":"Posts actually present on this day"}},"required":["date","expectedCount","actualCount"]},"PublicQueueResponseDto":{"type":"object","properties":{"windowStart":{"type":"string"},"windowEnd":{"type":"string"},"items":{"type":"array","items":{"$ref":"#/components/schemas/PublicQueueItemDto"}},"days":{"description":"Day-level coverage vs plan","type":"array","items":{"$ref":"#/components/schemas/PublicQueueDayDto"}},"hasActivePlan":{"type":"boolean"},"publishMode":{"type":"string","enum":["review","automatic"]},"pendingApprovalCount":{"type":"number","description":"Posts awaiting approval in the window"},"billingHold":{"type":"boolean","description":"True when scheduled posts are being held because the business has no active subscription; they will not publish until billing is restored"}},"required":["windowStart","windowEnd","items","days","hasActivePlan","pendingApprovalCount"]},"BrandColorsDto":{"type":"object","properties":{"primary":{"type":"string","example":"#1a1a2e","pattern":"^#[0-9a-fA-F]{6}$"},"secondary":{"type":"string","example":"#e94560","pattern":"^#[0-9a-fA-F]{6}$"},"accent":{"type":"string","example":"#0f3460","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["primary","secondary","accent"]},"UpdatePublicBrandDto":{"type":"object","properties":{"description":{"type":"string","maxLength":2000},"industry":{"type":"string","maxLength":200},"targetAudience":{"type":"string","maxLength":1000},"toneOfVoice":{"type":"array","description":"Tone-of-voice descriptors (the app can only render these values)","items":{"type":"string","enum":["professional","friendly","bold","casual","playful"]}},"colors":{"description":"The full palette (all three)","allOf":[{"$ref":"#/components/schemas/BrandColorsDto"}]},"keywords":{"type":"array","items":{"type":"string"}}}},"PublicBrandDto":{"type":"object","properties":{"businessName":{"type":"string"},"industry":{"type":"string"},"description":{"type":"string"},"targetAudience":{"type":"string"},"toneOfVoice":{"description":"Tone-of-voice descriptors","type":"array","items":{"type":"string"}},"colors":{"type":"object","description":"Brand palette {primary, secondary, accent}"},"colorsConfigured":{"type":"boolean","description":"False when the palette is still the unconfigured default, not an intentional brand choice"},"fonts":{"type":"object","description":"Brand fonts {heading, body}"},"fontsConfigured":{"type":"boolean","description":"False when the fonts are still the unconfigured default"},"logoUrl":{"type":"string","description":"Signed logo URL (expires ~6h, never cache)"},"keywords":{"type":"array","items":{"type":"string"}},"websiteUrl":{"type":"string"},"timezone":{"type":"object","description":"IANA timezone used for scheduling; null = uncaptured"},"images":{"type":"array","description":"Brand image library: attachment ids (stable, usable with set-image) plus signed URLs (expire ~6h) and content tags","items":{"type":"object","properties":{"attachmentId":{"type":"string"},"url":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}},"required":["businessName","toneOfVoice","colors","colorsConfigured","fonts","fontsConfigured","keywords","images"]},"SetBrandLogoDto":{"type":"object","properties":{"attachmentId":{"type":"string","description":"Attachment id of an uploaded image"}},"required":["attachmentId"]},"AddBrandMemoryDto":{"type":"object","properties":{"text":{"type":"string","description":"A durable brand fact (e.g. \"We never discount\")","maxLength":500}},"required":["text"]},"PublicBrandMemoryDto":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","description":"'fact' (durable) or 'timely' (decaying, agent-captured)"},"text":{"type":"string"},"expiresAt":{"type":"object","description":"When a timely note stops steering content"}},"required":["id","kind","text"]},"PublicBrandImageDto":{"type":"object","properties":{"attachmentId":{"type":"string","description":"Use with posts/{id}/set-image or posts/from-media"},"url":{"type":"string","description":"Signed URL; expires after ~6 hours"},"tags":{"description":"Content tags (e.g. \"product\", \"team\")","type":"array","items":{"type":"string"}}},"required":["attachmentId","url","tags"]},"PublicBrandImagesResponseDto":{"type":"object","properties":{"images":{"type":"array","items":{"$ref":"#/components/schemas/PublicBrandImageDto"}}},"required":["images"]},"PublicCalendarEntryDto":{"type":"object","properties":{"postId":{"type":"string","description":"Post id (use with GET /v1/public/posts/{id})"},"variantId":{"type":"string","description":"Variant id of the representative platform"},"date":{"type":"string","description":"Date (YYYY-MM-DD, business timezone)"},"name":{"type":"string","description":"Post name"},"status":{"type":"string","enum":["scheduled","published","generating"]},"platform":{"type":"string","description":"Representative platform"},"platforms":{"description":"All platforms the post targets","type":"array","items":{"type":"string"}},"scheduledAt":{"type":"string"},"publishedAt":{"type":"string"},"needsApproval":{"type":"boolean","description":"Awaiting approval before it can publish"},"permalink":{"type":"string","description":"Live URL when published"},"publishFailure":{"type":"object","description":"Publish failure rollup: {reason, failedAt} machine tokens"},"thumbnailUrl":{"type":"string","description":"Signed thumbnail URL (expires after ~6h, never cache)"}},"required":["postId","variantId","date","name","status","needsApproval"]},"PublicCalendarResponseDto":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/PublicCalendarEntryDto"}},"timezone":{"type":"string","description":"IANA timezone the dates are bucketed in"},"publishedLookbackDays":{"type":"number","description":"Published posts are included for at most the trailing 30 days, whatever the window asks for"}},"required":["entries","timezone","publishedLookbackDays"]},"PublicChannelsResponseDto":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/PublicChannelDto"}},"connectUrl":{"type":"string","description":"Connecting a channel requires a browser OAuth flow and cannot be done over the API. Open this URL in a browser to connect."}},"required":["channels","connectUrl"]},"ConnectLinkResponseDto":{"type":"object","properties":{"url":{"type":"string","description":"Open this URL in a browser to connect the channel. There is no headless connect: the platform OAuth flow needs the user."},"platform":{"type":"string","description":"Platform this link is for"}},"required":["url","platform"]},"PublicIdeaPostDto":{"type":"object","properties":{"postId":{"type":"string"},"shape":{"type":"string","description":"StaticImage | Carousel | Reel | TextOnly"},"status":{"type":"string","description":"generating | ready | failed"},"preview":{"type":"string","description":"First line of the drafted copy"}},"required":["postId","status"]},"PublicIdeaDto":{"type":"object","properties":{"id":{"type":"string"},"headline":{"type":"string"},"status":{"type":"string","description":"active | picked | dismissed"},"createdAt":{"format":"date-time","type":"string"},"posts":{"type":"array","items":{"$ref":"#/components/schemas/PublicIdeaPostDto"}}},"required":["id","headline","status","createdAt","posts"]},"PublicIdeasResponseDto":{"type":"object","properties":{"ideas":{"description":"Ideas (topics) with the suggested posts they spawned. Suggested posts are ephemeral until promoted; promote one to turn it into a real draft in /posts.","type":"array","items":{"$ref":"#/components/schemas/PublicIdeaDto"}}},"required":["ideas"]},"GenerateIdeasDto":{"type":"object","properties":{"count":{"type":"number","default":3,"minimum":1,"maximum":10},"direction":{"type":"string","description":"Steer the ideas (e.g. \"holiday specials\")","maxLength":500}}},"PublicJobAcceptedDto":{"type":"object","properties":{"jobId":{"type":"string","description":"Poll this job for the outcome"},"status":{"type":"string","example":"queued"},"kind":{"type":"string","example":"generate_post"},"resourceId":{"type":"string","description":"The resource the job acts on"},"poll":{"type":"string","example":"/v1/public/jobs/01J..."}},"required":["jobId","status","kind","poll"]},"PublicIdeaPickResponseDto":{"type":"object","properties":{"idea":{"$ref":"#/components/schemas/PublicIdeaDto"},"postsGenerating":{"type":"boolean","description":"True when the pick spawned background post generation (draws credits). Poll GET /ideas to watch the posts land."}},"required":["idea","postsGenerating"]},"PublicMediaItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Attachment id (stable)"},"url":{"type":"string","description":"Signed download URL. Expires after ~6 hours; never cache it."},"type":{"type":"string","enum":["image","video"]},"ratio":{"type":"number","description":"Aspect ratio (width/height)"},"altText":{"type":"string"}},"required":["id","type"]},"PublicGenerationStateDto":{"type":"object","properties":{"status":{"type":"string","enum":["pending","generating","ready","failed"],"description":"Content generation state. 'generating' resolves within ~15 minutes (a stuck generation is failed automatically)."},"error":{"type":"string","description":"Machine token for a failed generation"},"videoStatus":{"type":"string","enum":["generating","ready","failed"],"description":"Reel video rendering state (reels only; can settle after status is ready)"}},"required":["status"]},"PublicScoreDto":{"type":"object","properties":{"value":{"type":"object","description":"0-100 quality score (null for reels)"},"verdict":{"type":"object","description":"Engine verdict"},"singleFix":{"type":"object","description":"The one fix the engine would make first"}}},"PublicPublishFailureDto":{"type":"object","properties":{"reason":{"type":"string","description":"Machine reason: token_expired | no_channel | account_restricted | image_error | rate_limited | app_unaudited | unknown"},"failedAt":{"type":"string"}},"required":["reason","failedAt"]},"PublicPostVariantDto":{"type":"object","properties":{"id":{"type":"string","description":"Variant id (used by schedule/publish verbs)"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]},"isPrimary":{"type":"boolean","description":"The canonical variant other platforms derive from"},"status":{"type":"string","enum":["draft","scheduled","published"]},"body":{"type":"string","description":"Post body text (caption/message/text per platform)"},"title":{"type":"string","description":"Title (YouTube Shorts / Pinterest)"},"description":{"type":"string","description":"Description (YouTube Shorts / Pinterest)"},"hashtags":{"type":"array","items":{"type":"string"}},"altText":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/PublicMediaItemDto"}},"mediaOrigin":{"type":"string","enum":["taka","user"],"description":"'user' = your own uploaded media, published untouched"},"scheduledAt":{"type":"object"},"publishedAt":{"type":"object"},"permalink":{"type":"string","description":"Live URL of the published post"},"generation":{"$ref":"#/components/schemas/PublicGenerationStateDto"},"score":{"$ref":"#/components/schemas/PublicScoreDto"},"publishFailure":{"$ref":"#/components/schemas/PublicPublishFailureDto"},"pinned":{"type":"boolean","description":"User customized this variant; it no longer re-derives"}},"required":["id","platform","isPrimary","status","media","mediaOrigin","generation","pinned"]},"PublicPostDto":{"type":"object","properties":{"id":{"type":"string","description":"Post id"},"name":{"type":"string"},"kind":{"type":"string","enum":["post","carousel","reel","story","video"],"description":"The content shape of this post"},"status":{"type":"string","enum":["draft","scheduled","published"],"description":"Post-level rollup across variants"},"needsApproval":{"type":"boolean","description":"The post is gated on approval before it can publish (approve it via the approve verb or in the app)"},"idea":{"type":"string","description":"The idea/prompt behind the post"},"variants":{"type":"array","items":{"$ref":"#/components/schemas/PublicPostVariantDto"}},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","name","kind","status","needsApproval","variants","createdAt","updatedAt"]},"PublicJobDto":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","description":"What the job does","example":"generate_post"},"status":{"type":"string","enum":["queued","running","succeeded","failed"]},"resourceType":{"type":"string","description":"Resource the job acts on, e.g. 'post'"},"resourceId":{"type":"string"},"result":{"type":"object","description":"What changed, on success"},"error":{"type":"object","description":"{code, message} on failure"},"creditsCharged":{"type":"number","description":"Credits this job charged (on success only)"},"createdAt":{"format":"date-time","type":"string"},"completedAt":{"format":"date-time","type":"string"}},"required":["id","kind","status","creditsCharged","createdAt"]},"PublicJobsListResponseDto":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/PublicJobDto"}}},"required":["jobs"]},"PublicMediaDto":{"type":"object","properties":{"id":{"type":"string"},"mimeType":{"type":"string","example":"image/jpeg"},"size":{"type":"number","description":"Bytes"},"blurhash":{"type":"string","nullable":true},"ratio":{"type":"number","nullable":true,"description":"Width / height"},"url":{"type":"string","description":"Signed URL; expires after ~6 hours. Never cache it."},"urlExpiresAt":{"type":"string","description":"When the signed URL expires (ISO 8601)"}},"required":["id","mimeType","size","url"]},"PublicPostsListResponseDto":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/PublicPostDto"}},"nextCursor":{"type":"string","description":"Pass as ?cursor= to fetch the next page"}},"required":["posts"]},"PublicPostMetricsResponseDto":{"type":"object","properties":{"available":{"type":"boolean","description":"False when metrics could not be served for this post; see reason"},"reason":{"type":"string","description":"platform_not_supported | channel_not_connected | channel_token_unavailable | outside_metrics_window | analytics_not_enabled | post_not_published"},"metrics":{"$ref":"#/components/schemas/PublicPerformancePostDto"}},"required":["available"]},"CreatePostDto":{"type":"object","properties":{"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest"],"description":"Primary platform of the post"},"name":{"type":"string","description":"Post name (defaults to the idea, truncated)"},"idea":{"type":"string","description":"The idea/brief behind the post. Seeds AI generation when you call generate.","maxLength":2000}},"required":["platform"]},"CreatePostFromMediaDto":{"type":"object","properties":{"attachmentIds":{"description":"Your uploaded attachment ids, in display order. 1 video = reel; 1 image = post; 2-10 images = carousel. Uploads must belong to the same user identity as the API key.","type":"array","items":{"type":"string"}},"platforms":{"description":"Target platforms (validated against what the media shape supports)","type":"array","items":{"type":"string"}},"caption":{"type":"string","maxLength":3000},"name":{"type":"string","maxLength":200}},"required":["attachmentIds"]},"UpdatePostVariantDto":{"type":"object","properties":{"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"],"description":"Which platform variant to edit"},"body":{"type":"object","description":"Post body text. Maps to the field the platform actually publishes (caption / message / text / description). Send null to clear.","maxLength":5000},"title":{"type":"object","description":"Title (YouTube Shorts / Pinterest only)","maxLength":200},"description":{"type":"object","description":"Description (YouTube Shorts / Pinterest only)","maxLength":5000},"hashtags":{"description":"Without the # prefix","type":"array","items":{"type":"string"}},"altText":{"type":"object","maxLength":1000}},"required":["platform"]},"RenamePostDto":{"type":"object","properties":{"name":{"type":"string","maxLength":200}},"required":["name"]},"PublicVersionDto":{"type":"object","properties":{"seq":{"type":"number","description":"Version number (restore takes this)"},"kind":{"type":"string","description":"generate | ai | manual | restore"},"label":{"type":"object"},"feedback":{"type":"object","description":"The feedback that produced this version"},"score":{"type":"object","description":"0-100 quality score at this version"},"verdict":{"type":"object"},"createdAt":{"type":"string"}},"required":["seq","kind","createdAt"]},"PublicVersionsResponseDto":{"type":"object","properties":{"variantId":{"type":"string","description":"Variant the versions belong to"},"versions":{"type":"array","items":{"$ref":"#/components/schemas/PublicVersionDto"}}},"required":["variantId","versions"]},"SetPostImageDto":{"type":"object","properties":{"attachmentId":{"type":"string","description":"Attachment id (from your media uploads or the brand image library)"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"],"description":"Defaults to the primary variant"}},"required":["attachmentId"]},"GeneratePostBodyDto":{"type":"object","properties":{"idea":{"type":"string","description":"Idea/brief for the generation (defaults to the post idea)"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"],"description":"Defaults to the primary variant"}}},"RevisePostBodyDto":{"type":"object","properties":{"feedback":{"type":"string","description":"Natural-language feedback; the engine keeps what works"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]}},"required":["feedback"]},"RegenerateImageBodyDto":{"type":"object","properties":{"hint":{"type":"string","description":"What the new image should change"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"]}}},"SchedulePostDto":{"type":"object","properties":{"scheduledAt":{"type":"string","description":"When to publish (ISO timestamp, must be in the future)"},"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"],"description":"Defaults to the primary variant"}},"required":["scheduledAt"]},"ScheduledVariantDto":{"type":"object","properties":{"variantId":{"type":"string"},"platform":{"type":"string"},"scheduledAt":{"type":"object","description":"The scheduled slot (null after unschedule)"},"eligibleToPublish":{"type":"boolean","description":"Whether the post will actually publish at its slot right now (billing, approval, plan-pause, and staleness gates all pass). False means it is held; the post envelope's needsApproval and the account's plan explain why."},"needsApproval":{"type":"boolean","description":"Awaiting approval before it can publish"}},"required":["variantId","platform","eligibleToPublish","needsApproval"]},"VariantScopedDto":{"type":"object","properties":{"platform":{"type":"string","enum":["instagram","facebook","linkedin","tiktok","youtube_shorts","pinterest","x"],"description":"Defaults to the primary variant"}}},"ApproveResultDto":{"type":"object","properties":{"variantId":{"type":"string"},"approved":{"type":"boolean","description":"Approval is stamped post-wide (on the post, not one variant)"},"publishedImmediately":{"type":"boolean","description":"True when the slot had already passed and approving published it immediately (the missed-slot recovery)"},"scheduledAt":{"type":"object"}},"required":["variantId","approved","publishedImmediately"]},"PublishResultDto":{"type":"object","properties":{"variantId":{"type":"string"},"platform":{"type":"string"},"success":{"type":"boolean","description":"Whether the platform accepted the post"},"permalink":{"type":"string","description":"Live URL of the published post"},"postId":{"type":"string","description":"Platform post/media id"},"error":{"type":"string","description":"Why the publish failed (plain language)"},"retryable":{"type":"boolean","description":"Whether retrying might succeed"}},"required":["variantId","platform","success"]}}}}