# generated by datamodel-codegen:
#   filename:  openapi.yaml
#   timestamp: 2026-03-21T13:00:33+00:00

from __future__ import annotations

from datetime import date as date_aliased
from enum import Enum
from typing import Annotated, Any, Dict, List

from pydantic import AnyUrl, AwareDatetime, BaseModel, Field


class ErrorResponse(BaseModel):
    error: str | None = None
    details: Dict[str, Any] | None = None


class FoodMenuLabel(BaseModel):
    displayName: str
    """
    Display name of the item/section/menu
    """
    description: str | None = None
    """
    Optional description
    """
    languageCode: str | None = None
    """
    BCP-47 language code (e.g. en, es)
    """


class Money(BaseModel):
    currencyCode: str
    """
    ISO 4217 currency code (e.g. USD, EUR)
    """
    units: str
    """
    Whole units of the amount
    """
    nanos: int | None = None
    """
    Nano units (10^-9) of the amount
    """


class FoodMenuItemAttributes(BaseModel):
    price: Money | None = None
    spiciness: str | None = None
    """
    Spiciness level (e.g. MILD, MEDIUM, HOT)
    """
    allergen: List[str] | None = None
    """
    Allergens (e.g. DAIRY, GLUTEN, SHELLFISH)
    """
    dietaryRestriction: List[str] | None = None
    """
    Dietary labels (e.g. VEGETARIAN, VEGAN, GLUTEN_FREE)
    """
    servesNumPeople: int | None = None
    """
    Number of people the item serves
    """
    preparationMethods: List[str] | None = None
    """
    Preparation methods (e.g. GRILLED, FRIED)
    """
    mediaKeys: List[str] | None = None
    """
    Media references for item photos
    """


class Option(BaseModel):
    labels: List[FoodMenuLabel] | None = None
    attributes: FoodMenuItemAttributes | None = None


class FoodMenuItem(BaseModel):
    labels: List[FoodMenuLabel]
    attributes: FoodMenuItemAttributes | None = None
    options: List[Option] | None = None
    """
    Item variants/options (e.g. sizes, preparations)
    """


class FoodMenuSection(BaseModel):
    labels: List[FoodMenuLabel]
    items: List[FoodMenuItem] | None = None


class FoodMenu(BaseModel):
    labels: List[FoodMenuLabel]
    sections: List[FoodMenuSection] | None = None
    cuisines: List[str] | None = None
    """
    Cuisine types (e.g. AMERICAN, ITALIAN, JAPANESE)
    """
    sourceUrl: str | None = None
    """
    URL of the original menu source
    """


class DateRange(BaseModel):
    startDate: date_aliased | None = None
    endDate: date_aliased | None = None


class DailyView(BaseModel):
    date: date_aliased | None = None
    views: int | None = None
    estimatedMinutesWatched: float | None = None
    averageViewDuration: float | None = None
    """
    Average view duration in seconds
    """
    subscribersGained: int | None = None
    subscribersLost: int | None = None
    likes: int | None = None
    comments: int | None = None
    shares: int | None = None


class ScopeStatus(BaseModel):
    hasAnalyticsScope: bool | None = None


class YouTubeDailyViewsResponse(BaseModel):
    success: Annotated[bool | None, Field(examples=[True])] = None
    videoId: str | None = None
    """
    The YouTube video ID
    """
    dateRange: DateRange | None = None
    totalViews: int | None = None
    """
    Sum of views across all days in the range
    """
    dailyViews: List[DailyView] | None = None
    lastSyncedAt: AwareDatetime | None = None
    """
    When the data was last synced from YouTube
    """
    scopeStatus: ScopeStatus | None = None


class ScopeStatus1(BaseModel):
    hasAnalyticsScope: Annotated[bool | None, Field(examples=[False])] = None
    requiresReauthorization: Annotated[bool | None, Field(examples=[True])] = None
    reauthorizeUrl: AnyUrl | None = None
    """
    URL to redirect user for reauthorization
    """


class YouTubeScopeMissingResponse(BaseModel):
    success: Annotated[bool | None, Field(examples=[False])] = None
    error: Annotated[
        str | None,
        Field(
            examples=[
                "To access daily video analytics, please reconnect your YouTube account to grant the required permissions."
            ]
        ),
    ] = None
    code: Annotated[str | None, Field(examples=["youtube_analytics_scope_missing"])] = (
        None
    )
    scopeStatus: ScopeStatus1 | None = None


class Event(Enum):
    POST_SCHEDULED = "post.scheduled"
    POST_PUBLISHED = "post.published"
    POST_FAILED = "post.failed"
    POST_PARTIAL = "post.partial"
    POST_RECYCLED = "post.recycled"
    ACCOUNT_CONNECTED = "account.connected"
    ACCOUNT_DISCONNECTED = "account.disconnected"
    MESSAGE_RECEIVED = "message.received"
    COMMENT_RECEIVED = "comment.received"


class Webhook(BaseModel):
    """
    Individual webhook configuration for receiving real-time notifications
    """

    field_id: Annotated[str | None, Field(alias="_id")] = None
    """
    Unique webhook identifier
    """
    name: Annotated[str | None, Field(max_length=50)] = None
    """
    Webhook name (for identification)
    """
    url: AnyUrl | None = None
    """
    Webhook endpoint URL
    """
    secret: str | None = None
    """
    Secret key for HMAC-SHA256 signature (not returned in responses for security)
    """
    events: List[Event] | None = None
    """
    Events subscribed to
    """
    isActive: bool | None = None
    """
    Whether webhook delivery is enabled
    """
    lastFiredAt: AwareDatetime | None = None
    """
    Timestamp of last successful webhook delivery
    """
    failureCount: int | None = None
    """
    Consecutive delivery failures (resets on success, webhook disabled at 10)
    """
    customHeaders: Dict[str, str] | None = None
    """
    Custom headers included in webhook requests
    """


class Event1(Enum):
    POST_SCHEDULED = "post.scheduled"
    POST_PUBLISHED = "post.published"
    POST_FAILED = "post.failed"
    POST_PARTIAL = "post.partial"
    POST_RECYCLED = "post.recycled"
    ACCOUNT_CONNECTED = "account.connected"
    ACCOUNT_DISCONNECTED = "account.disconnected"
    MESSAGE_RECEIVED = "message.received"
    COMMENT_RECEIVED = "comment.received"
    WEBHOOK_TEST = "webhook.test"


class Status(Enum):
    SUCCESS = "success"
    FAILED = "failed"


class WebhookLog(BaseModel):
    """
    Webhook delivery log entry
    """

    field_id: Annotated[str | None, Field(alias="_id")] = None
    webhookId: str | None = None
    """
    ID of the webhook that was triggered
    """
    webhookName: str | None = None
    """
    Name of the webhook that was triggered
    """
    event: Event1 | None = None
    url: AnyUrl | None = None
    status: Status | None = None
    statusCode: int | None = None
    """
    HTTP status code from webhook endpoint
    """
    requestPayload: Dict[str, Any] | None = None
    """
    Payload sent to webhook endpoint
    """
    responseBody: str | None = None
    """
    Response body from webhook endpoint (truncated to 10KB)
    """
    errorMessage: str | None = None
    """
    Error message if delivery failed
    """
    attemptNumber: int | None = None
    """
    Delivery attempt number (max 3 retries)
    """
    responseTime: int | None = None
    """
    Response time in milliseconds
    """
    createdAt: AwareDatetime | None = None


class Event2(Enum):
    POST_SCHEDULED = "post.scheduled"
    POST_PUBLISHED = "post.published"
    POST_FAILED = "post.failed"
    POST_PARTIAL = "post.partial"
    POST_RECYCLED = "post.recycled"


class Platform(BaseModel):
    platform: str | None = None
    status: str | None = None
    publishedUrl: str | None = None
    error: str | None = None


class Post(BaseModel):
    id: str | None = None
    content: str | None = None
    status: str | None = None
    scheduledFor: AwareDatetime | None = None
    publishedAt: AwareDatetime | None = None
    platforms: List[Platform] | None = None


class WebhookPayloadPost(BaseModel):
    """
    Webhook payload for post events
    """

    event: Event2 | None = None
    post: Post | None = None
    timestamp: AwareDatetime | None = None


class Event3(Enum):
    ACCOUNT_CONNECTED = "account.connected"


class Account(BaseModel):
    accountId: str | None = None
    """
    The account's unique identifier (same as used in /v1/accounts/{accountId})
    """
    profileId: str | None = None
    """
    The profile's unique identifier this account belongs to
    """
    platform: str | None = None
    username: str | None = None
    displayName: str | None = None


class WebhookPayloadAccountConnected(BaseModel):
    """
    Webhook payload for account connected events
    """

    event: Event3 | None = None
    account: Account | None = None
    timestamp: AwareDatetime | None = None


class Event4(Enum):
    ACCOUNT_DISCONNECTED = "account.disconnected"


class DisconnectionType(Enum):
    """
    Whether the disconnection was intentional (user action) or unintentional (token expired/revoked)
    """

    INTENTIONAL = "intentional"
    UNINTENTIONAL = "unintentional"


class Account1(BaseModel):
    accountId: str | None = None
    """
    The account's unique identifier (same as used in /v1/accounts/{accountId})
    """
    profileId: str | None = None
    """
    The profile's unique identifier this account belongs to
    """
    platform: str | None = None
    username: str | None = None
    displayName: str | None = None
    disconnectionType: DisconnectionType | None = None
    """
    Whether the disconnection was intentional (user action) or unintentional (token expired/revoked)
    """
    reason: str | None = None
    """
    Human-readable reason for the disconnection
    """


class WebhookPayloadAccountDisconnected(BaseModel):
    """
    Webhook payload for account disconnected events
    """

    event: Event4 | None = None
    account: Account1 | None = None
    timestamp: AwareDatetime | None = None


class Event5(Enum):
    COMMENT_RECEIVED = "comment.received"


class Platform1(Enum):
    INSTAGRAM = "instagram"
    FACEBOOK = "facebook"
    TWITTER = "twitter"
    YOUTUBE = "youtube"
    LINKEDIN = "linkedin"
    BLUESKY = "bluesky"
    REDDIT = "reddit"


class Author(BaseModel):
    id: str | None = None
    """
    Author's platform ID
    """
    username: str | None = None
    name: str | None = None
    picture: str | None = None


class Comment(BaseModel):
    id: str | None = None
    """
    Platform comment ID
    """
    postId: str | None = None
    """
    Internal post ID
    """
    platformPostId: str | None = None
    """
    Platform's post ID
    """
    platform: Platform1 | None = None
    text: str | None = None
    """
    Comment text content
    """
    author: Author | None = None
    createdAt: AwareDatetime | None = None
    isReply: bool | None = None
    """
    Whether this is a reply to another comment
    """
    parentCommentId: str | None = None
    """
    Parent comment ID if this is a reply
    """


class Post1(BaseModel):
    id: str | None = None
    """
    Internal post ID
    """
    platformPostId: str | None = None
    """
    Platform's post ID
    """


class Account2(BaseModel):
    id: str | None = None
    """
    Social account ID
    """
    platform: str | None = None
    username: str | None = None


class WebhookPayloadComment(BaseModel):
    """
    Webhook payload for comment received events (Instagram, Facebook, Twitter/X, YouTube, LinkedIn, Bluesky, Reddit)
    """

    event: Event5 | None = None
    comment: Comment | None = None
    post: Post1 | None = None
    account: Account2 | None = None
    timestamp: AwareDatetime | None = None


class Event6(Enum):
    MESSAGE_RECEIVED = "message.received"


class Platform2(Enum):
    INSTAGRAM = "instagram"
    FACEBOOK = "facebook"
    TELEGRAM = "telegram"
    BLUESKY = "bluesky"
    REDDIT = "reddit"


class Direction(Enum):
    INCOMING = "incoming"


class Attachment(BaseModel):
    type: str | None = None
    """
    Attachment type (image, video, file, sticker, audio)
    """
    url: str | None = None
    """
    Attachment URL (may expire for Meta platforms)
    """
    payload: Dict[str, Any] | None = None
    """
    Additional attachment metadata
    """


class InstagramProfile(BaseModel):
    """
    Instagram profile data for the sender. Only present for Instagram conversations.
    """

    isFollower: bool | None = None
    """
    Whether the sender follows your Instagram business account
    """
    isFollowing: bool | None = None
    """
    Whether your Instagram business account follows the sender
    """
    followerCount: int | None = None
    """
    The sender's follower count on Instagram
    """
    isVerified: bool | None = None
    """
    Whether the sender is a verified Instagram user
    """


class Sender(BaseModel):
    id: str | None = None
    name: str | None = None
    username: str | None = None
    picture: str | None = None
    instagramProfile: InstagramProfile | None = None
    """
    Instagram profile data for the sender. Only present for Instagram conversations.
    """


class Message(BaseModel):
    id: str | None = None
    """
    Internal message ID
    """
    conversationId: str | None = None
    """
    Internal conversation ID
    """
    platform: Platform2 | None = None
    platformMessageId: str | None = None
    """
    Platform's message ID
    """
    direction: Direction | None = None
    text: str | None = None
    """
    Message text content
    """
    attachments: List[Attachment] | None = None
    sender: Sender | None = None
    sentAt: AwareDatetime | None = None
    isRead: bool | None = None


class Status1(Enum):
    ACTIVE = "active"
    ARCHIVED = "archived"


class Conversation(BaseModel):
    id: str | None = None
    platformConversationId: str | None = None
    participantId: str | None = None
    participantName: str | None = None
    participantUsername: str | None = None
    participantPicture: str | None = None
    status: Status1 | None = None


class Account3(BaseModel):
    id: str | None = None
    """
    Social account ID
    """
    platform: str | None = None
    username: str | None = None
    displayName: str | None = None


class Metadata(BaseModel):
    """
    Interactive message metadata (present when message is a quick reply tap, postback button tap, or inline keyboard callback)
    """

    quickReplyPayload: str | None = None
    """
    Payload from a quick reply tap (Meta platforms)
    """
    postbackPayload: str | None = None
    """
    Payload from a postback button tap (Meta platforms)
    """
    postbackTitle: str | None = None
    """
    Title of the tapped postback button (Meta platforms)
    """
    callbackData: str | None = None
    """
    Callback data from an inline keyboard button tap (Telegram)
    """


class WebhookPayloadMessage(BaseModel):
    """
    Webhook payload for message received events (DMs from Instagram, Facebook, Telegram, Bluesky, Reddit)
    """

    event: Event6 | None = None
    message: Message | None = None
    conversation: Conversation | None = None
    account: Account3 | None = None
    metadata: Metadata | None = None
    """
    Interactive message metadata (present when message is a quick reply tap, postback button tap, or inline keyboard callback)
    """
    timestamp: AwareDatetime | None = None


class PostId(BaseModel):
    """
    Populated post reference
    """

    field_id: Annotated[str | None, Field(alias="_id")] = None
    content: str | None = None
    status: str | None = None


class Platform3(Enum):
    TIKTOK = "tiktok"
    INSTAGRAM = "instagram"
    FACEBOOK = "facebook"
    YOUTUBE = "youtube"
    LINKEDIN = "linkedin"
    TWITTER = "twitter"
    THREADS = "threads"
    PINTEREST = "pinterest"
    REDDIT = "reddit"
    BLUESKY = "bluesky"
    GOOGLEBUSINESS = "googlebusiness"
    TELEGRAM = "telegram"
    SNAPCHAT = "snapchat"


class Action(Enum):
    """
    Type of action logged: publish (initial attempt), retry (after failure), media_upload, rate_limit_pause, token_refresh, cancelled
    """

    PUBLISH = "publish"
    RETRY = "retry"
    MEDIA_UPLOAD = "media_upload"
    RATE_LIMIT_PAUSE = "rate_limit_pause"
    TOKEN_REFRESH = "token_refresh"
    CANCELLED = "cancelled"


class Status2(Enum):
    SUCCESS = "success"
    FAILED = "failed"
    PENDING = "pending"
    SKIPPED = "skipped"


class Request(BaseModel):
    contentPreview: str | None = None
    """
    First 200 chars of caption
    """
    mediaCount: int | None = None
    mediaTypes: List[str] | None = None
    mediaUrls: List[str] | None = None
    """
    URLs of media items sent to platform
    """
    scheduledFor: AwareDatetime | None = None
    rawBody: str | None = None
    """
    Full request body JSON (max 5000 chars)
    """


class Response(BaseModel):
    platformPostId: str | None = None
    """
    ID returned by platform on success
    """
    platformPostUrl: str | None = None
    """
    URL of published post
    """
    errorMessage: str | None = None
    """
    Error message on failure
    """
    errorCode: str | None = None
    """
    Platform-specific error code
    """
    rawBody: str | None = None
    """
    Full response body JSON (max 5000 chars)
    """


class PostLog(BaseModel):
    """
    Publishing log entry showing details of a post publishing attempt
    """

    field_id: Annotated[str | None, Field(alias="_id")] = None
    postId: str | PostId | None = None
    userId: str | None = None
    profileId: str | None = None
    platform: Platform3 | None = None
    accountId: str | None = None
    accountUsername: str | None = None
    action: Action | None = None
    """
    Type of action logged: publish (initial attempt), retry (after failure), media_upload, rate_limit_pause, token_refresh, cancelled
    """
    status: Status2 | None = None
    statusCode: int | None = None
    """
    HTTP status code from platform API
    """
    endpoint: str | None = None
    """
    Platform API endpoint called
    """
    request: Request | None = None
    response: Response | None = None
    durationMs: int | None = None
    """
    How long the operation took in milliseconds
    """
    attemptNumber: int | None = None
    """
    Attempt number (1 for first try, 2+ for retries)
    """
    createdAt: AwareDatetime | None = None


class EventType(Enum):
    """
    Type of connection event: connect_success, connect_failed, disconnect, reconnect_success, reconnect_failed
    """

    CONNECT_SUCCESS = "connect_success"
    CONNECT_FAILED = "connect_failed"
    DISCONNECT = "disconnect"
    RECONNECT_SUCCESS = "reconnect_success"
    RECONNECT_FAILED = "reconnect_failed"


class ConnectionMethod(Enum):
    """
    How the connection was initiated
    """

    OAUTH = "oauth"
    CREDENTIALS = "credentials"
    INVITATION = "invitation"


class Error(BaseModel):
    """
    Error details (present on failed events)
    """

    code: str | None = None
    """
    Error code (e.g., oauth_denied, token_exchange_failed)
    """
    message: str | None = None
    """
    Human-readable error message
    """
    rawResponse: str | None = None
    """
    Raw error response (truncated to 2000 chars)
    """


class Success(BaseModel):
    """
    Success details (present on successful events)
    """

    displayName: str | None = None
    username: str | None = None
    profilePicture: str | None = None
    permissions: List[str] | None = None
    """
    OAuth scopes/permissions granted
    """
    tokenExpiresAt: AwareDatetime | None = None
    accountType: str | None = None
    """
    Account type (personal, business, organization)
    """


class Context(BaseModel):
    """
    Additional context about the connection attempt
    """

    isHeadlessMode: bool | None = None
    hasCustomRedirectUrl: bool | None = None
    isReconnection: bool | None = None
    isBYOK: bool | None = None
    """
    Using bring-your-own-keys
    """
    invitationToken: str | None = None
    connectToken: str | None = None


class ConnectionLog(BaseModel):
    """
    Connection event log showing account connection/disconnection history
    """

    field_id: Annotated[str | None, Field(alias="_id")] = None
    userId: str | None = None
    """
    User who owns the connection (may be null for early OAuth failures)
    """
    profileId: str | None = None
    accountId: str | None = None
    """
    The social account ID (present on successful connections and disconnects)
    """
    platform: Platform3 | None = None
    eventType: EventType | None = None
    """
    Type of connection event: connect_success, connect_failed, disconnect, reconnect_success, reconnect_failed
    """
    connectionMethod: ConnectionMethod | None = None
    """
    How the connection was initiated
    """
    error: Error | None = None
    """
    Error details (present on failed events)
    """
    success: Success | None = None
    """
    Success details (present on successful events)
    """
    context: Context | None = None
    """
    Additional context about the connection attempt
    """
    durationMs: int | None = None
    """
    How long the operation took in milliseconds
    """
    metadata: Dict[str, Any] | None = None
    """
    Additional metadata
    """
    createdAt: AwareDatetime | None = None


class Type(Enum):
    IMAGE = "image"
    VIDEO = "video"
    GIF = "gif"
    DOCUMENT = "document"


class MediaItem(BaseModel):
    """
    Media referenced in posts. URLs must be publicly reachable over HTTPS. Use POST /v1/media/presign for uploads up to 5GB. Zernio auto-compresses images and videos that exceed platform limits (videos over 200 MB may not be compressed).
    """

    type: Type | None = None
    url: AnyUrl | None = None
    title: str | None = None
    """
    Optional title for the media item. Used as the document title for LinkedIn PDF/carousel posts. If omitted, falls back to the post title, then the filename.
    """
    filename: str | None = None
    size: int | None = None
    """
    Optional file size in bytes
    """
    mimeType: str | None = None
    """
    Optional MIME type (e.g. image/jpeg, video/mp4)
    """
    thumbnail: AnyUrl | None = None
    """
    Optional custom thumbnail/cover image URL for videos. Supported for Facebook video posts, Facebook Reels, and regular video uploads. Max 10MB, JPG/PNG recommended.
    """
    instagramThumbnail: AnyUrl | None = None
    """
    Optional custom cover image URL for Instagram Reels
    """
    tiktokProcessed: bool | None = None
    """
    Internal flag indicating the image was resized for TikTok
    """


class ErrorCategory(Enum):
    """
    Error category for programmatic handling: auth_expired (token expired/revoked), user_content (wrong format/too long), user_abuse (rate limits/spam), account_issue (config problems), platform_rejected (policy violation), platform_error (5xx/maintenance), system_error (Zernio infra), unknown
    """

    AUTH_EXPIRED = "auth_expired"
    USER_CONTENT = "user_content"
    USER_ABUSE = "user_abuse"
    ACCOUNT_ISSUE = "account_issue"
    PLATFORM_REJECTED = "platform_rejected"
    PLATFORM_ERROR = "platform_error"
    SYSTEM_ERROR = "system_error"
    UNKNOWN = "unknown"


class ErrorSource(Enum):
    """
    Who caused the error: user (fix content/reconnect), platform (outage/API change), system (Zernio issue, rare)
    """

    USER = "user"
    PLATFORM = "platform"
    SYSTEM = "system"


class Status3(Enum):
    DRAFT = "draft"
    SCHEDULED = "scheduled"
    PUBLISHING = "publishing"
    PUBLISHED = "published"
    FAILED = "failed"
    PARTIAL = "partial"


class Visibility(Enum):
    PUBLIC = "public"
    PRIVATE = "private"
    UNLISTED = "unlisted"


class GapFreq(Enum):
    """
    Interval unit for the gap. Defaults to 'month'.
    """

    WEEK = "week"
    MONTH = "month"


class RecyclingConfig(BaseModel):
    """
    Configure automatic post recycling (reposting at regular intervals).
    After the post is published, the system creates new scheduled copies at the
    specified interval until expiration conditions are met. Supports weekly or
    monthly intervals. Maximum 10 active recycling posts per account.
    YouTube and TikTok platforms are excluded from recycling.
    Content variations are recommended for Twitter and Pinterest to avoid duplicate flags.

    """

    enabled: bool = True
    """
    Set to false to disable recycling on this post
    """
    gap: Annotated[int | None, Field(examples=[2], ge=1)] = None
    """
    Number of interval units between each repost. Required when enabling recycling.
    """
    gapFreq: GapFreq = "month"
    """
    Interval unit for the gap. Defaults to 'month'.
    """
    startDate: AwareDatetime | None = None
    """
    When to start the recycling cycle. Defaults to the post's scheduledFor date.
    """
    expireCount: Annotated[int | None, Field(examples=[5], ge=1)] = None
    """
    Stop recycling after this many copies have been created
    """
    expireDate: AwareDatetime | None = None
    """
    Stop recycling after this date, regardless of count
    """
    contentVariations: Annotated[List[str] | None, Field(max_length=20)] = None
    """
    Array of content variations for recycled copies. On each recycle, the next
    variation is used in round-robin order. Recommended for Twitter and Pinterest
    to avoid duplicate content flags. If omitted, the original post content is
    used for all recycled copies. Send an empty array [] to clear existing
    variations. Must have 2+ entries when setting variations. Platform-level
    customContent still overrides the base content per platform.

    """


class GapFreq1(Enum):
    """
    Interval unit (week or month)
    """

    WEEK = "week"
    MONTH = "month"


class RecyclingState(BaseModel):
    """
    Current recycling configuration and state on a post
    """

    enabled: bool | None = None
    """
    Whether recycling is currently active
    """
    gap: int | None = None
    """
    Number of interval units between reposts
    """
    gapFreq: GapFreq1 | None = None
    """
    Interval unit (week or month)
    """
    startDate: AwareDatetime | None = None
    expireCount: int | None = None
    expireDate: AwareDatetime | None = None
    contentVariations: List[str] | None = None
    """
    Content variations for recycled copies (if configured)
    """
    contentVariationIndex: int | None = None
    """
    Current position in the content variations rotation (read-only)
    """
    recycleCount: int | None = None
    """
    How many recycled copies have been created so far (read-only)
    """
    nextRecycleAt: AwareDatetime | None = None
    """
    When the next recycled copy will be created (read-only)
    """
    lastRecycledAt: AwareDatetime | None = None
    """
    When the last recycled copy was created (read-only)
    """


class ReplySettings(Enum):
    """
    Controls who can reply to the tweet. "following" allows only people you follow, "mentionedUsers" allows only mentioned users, "subscribers" allows only subscribers, "verified" allows only verified users. Omit for default (everyone can reply). For threads, applies to the first tweet only. Cannot be combined with replyToTweetId.
    """

    FOLLOWING = "following"
    MENTIONED_USERS = "mentionedUsers"
    SUBSCRIBERS = "subscribers"
    VERIFIED = "verified"


class ThreadItem(BaseModel):
    content: str | None = None
    mediaItems: List[MediaItem] | None = None


class TwitterPlatformData(BaseModel):
    replyToTweetId: str | None = None
    """
    ID of an existing tweet to reply to. The published tweet will appear as a reply in that tweet's thread. For threads, only the first tweet replies to the target; subsequent tweets chain normally.
    """
    replySettings: ReplySettings | None = None
    """
    Controls who can reply to the tweet. "following" allows only people you follow, "mentionedUsers" allows only mentioned users, "subscribers" allows only subscribers, "verified" allows only verified users. Omit for default (everyone can reply). For threads, applies to the first tweet only. Cannot be combined with replyToTweetId.
    """
    threadItems: List[ThreadItem] | None = None
    """
    Sequence of tweets in a thread. First item is the root tweet.
    """


class ThreadsPlatformData(BaseModel):
    """
    Up to 10 images per carousel (no videos). Videos must be H.264/AAC MP4, max 5 min. Images JPEG/PNG, max 8 MB. Use threadItems for reply chains.
    """

    threadItems: List[ThreadItem] | None = None
    """
    Sequence of posts in a Threads thread (root then replies in order).
    """


class ContentType(Enum):
    """
    Set to 'story' for Page Stories (24h ephemeral) or 'reel' for Reels (short vertical video). Defaults to feed post if omitted.
    """

    STORY = "story"
    REEL = "reel"


class FacebookPlatformData(BaseModel):
    """
    Feed posts support up to 10 images (no mixed video+image). Stories require single media (24h, no captions). Reels require single vertical video (9:16, 3-60s).
    """

    contentType: ContentType | None = None
    """
    Set to 'story' for Page Stories (24h ephemeral) or 'reel' for Reels (short vertical video). Defaults to feed post if omitted.
    """
    title: str | None = None
    """
    Reel title (only for contentType=reel). Separate from the caption/content field.
    """
    firstComment: str | None = None
    """
    Optional first comment to post immediately after publishing (feed posts only, not stories or reels)
    """
    pageId: str | None = None
    """
    Target Facebook Page ID for multi-page posting. If omitted, uses the default page. Use GET /v1/accounts/{id}/facebook-page to list pages.
    """


class ContentType1(Enum):
    """
    Set to 'story' to publish as a Story. Default posts become Reels or feed depending on media.
    """

    STORY = "story"


class GraduationStrategy(Enum):
    """
    MANUAL (graduate from Instagram app) or SS_PERFORMANCE (auto-graduate if performs well with non-followers)
    """

    MANUAL = "MANUAL"
    SS_PERFORMANCE = "SS_PERFORMANCE"


class TrialParams(BaseModel):
    """
    Trial Reels configuration. Trial reels are shared to non-followers first and can later be graduated to regular reels manually or automatically based on performance. Only applies to Reels.
    """

    graduationStrategy: GraduationStrategy | None = None
    """
    MANUAL (graduate from Instagram app) or SS_PERFORMANCE (auto-graduate if performs well with non-followers)
    """


class UserTag(BaseModel):
    username: Annotated[str, Field(examples=["friend_username"])]
    """
    Instagram username (@ symbol is optional and will be removed automatically)
    """
    x: Annotated[float, Field(examples=[0.5], ge=0.0, le=1.0)]
    """
    X coordinate position from left edge (0.0 = left, 0.5 = center, 1.0 = right)
    """
    y: Annotated[float, Field(examples=[0.5], ge=0.0, le=1.0)]
    """
    Y coordinate position from top edge (0.0 = top, 0.5 = center, 1.0 = bottom)
    """
    mediaIndex: Annotated[int | None, Field(examples=[0], ge=0)] = None
    """
    Zero-based index of the carousel item to tag. Defaults to 0. Tags on video items or out-of-range indices are ignored.
    """


class InstagramPlatformData(BaseModel):
    """
    Feed aspect ratio 0.8-1.91, carousels up to 10 items, stories require media (no captions). User tag coordinates 0.0-1.0 from top-left. Images over 8 MB and videos over platform limits are auto-compressed.
    """

    contentType: ContentType1 | None = None
    """
    Set to 'story' to publish as a Story. Default posts become Reels or feed depending on media.
    """
    shareToFeed: bool = True
    """
    For Reels only. When true (default), the Reel appears on both the Reels tab and your main profile feed. Set to false to post to the Reels tab only.
    """
    collaborators: List[str] | None = None
    """
    Up to 3 Instagram usernames to invite as collaborators (feed/Reels only)
    """
    firstComment: str | None = None
    """
    Optional first comment to add after the post is created (not applied to Stories)
    """
    trialParams: TrialParams | None = None
    """
    Trial Reels configuration. Trial reels are shared to non-followers first and can later be graduated to regular reels manually or automatically based on performance. Only applies to Reels.
    """
    userTags: List[UserTag] | None = None
    """
    Tag Instagram users in photos by username and position. Not supported for stories or videos. For carousels, use mediaIndex to target specific slides (defaults to 0). Tags on video items are silently skipped.
    """
    audioName: Annotated[str | None, Field(examples=["My Podcast Intro"])] = None
    """
    Custom name for original audio in Reels. Replaces the default "Original Audio" label. Can only be set once.
    """
    thumbOffset: Annotated[int | None, Field(examples=[5000], ge=0)] = None
    """
    Millisecond offset from video start for the Reel thumbnail. Ignored if a custom thumbnail URL is provided. Defaults to 0.
    """


class LinkedInPlatformData(BaseModel):
    """
    Up to 20 images, no multi-video. Single PDF supported (max 100MB). Link previews auto-generated when no media attached. Use organizationUrn for multi-org posting.
    """

    documentTitle: str | None = None
    """
    Title displayed on LinkedIn document (PDF/carousel) posts. Required by LinkedIn for document posts. If omitted, falls back to the media item title, then the filename.
    """
    organizationUrn: str | None = None
    """
    Target LinkedIn Organization URN (e.g. "urn:li:organization:123456789"). If omitted, uses the default org. Use GET /v1/accounts/{id}/linkedin-organizations to list orgs.
    """
    firstComment: str | None = None
    """
    Optional first comment to add after the post is created
    """
    disableLinkPreview: bool | None = None
    """
    Set to true to disable automatic link previews for URLs in the post content (default is false)
    """


class PinterestPlatformData(BaseModel):
    title: Annotated[str | None, Field(max_length=100)] = None
    """
    Pin title. Defaults to first line of content or "Pin". Must be ≤ 100 characters.
    """
    boardId: str | None = None
    """
    Target Pinterest board ID. If omitted, the first available board is used.
    """
    link: AnyUrl | None = None
    """
    Destination link (pin URL)
    """
    coverImageUrl: AnyUrl | None = None
    """
    Optional cover image for video pins
    """
    coverImageKeyFrameTime: int | None = None
    """
    Optional key frame time in seconds for derived video cover
    """


class Visibility1(Enum):
    """
    Video visibility: public (default, anyone can watch), unlisted (link only), private (invite only)
    """

    PUBLIC = "public"
    PRIVATE = "private"
    UNLISTED = "unlisted"


class YouTubePlatformData(BaseModel):
    """
    Videos under 3 min auto-detected as Shorts. Custom thumbnails for regular videos only. Scheduled videos are uploaded immediately with the specified visibility.
    """

    title: Annotated[str | None, Field(max_length=100)] = None
    """
    Video title. Defaults to first line of content or "Untitled Video". Must be ≤ 100 characters.
    """
    visibility: Visibility1 = "public"
    """
    Video visibility: public (default, anyone can watch), unlisted (link only), private (invite only)
    """
    madeForKids: bool = False
    """
    COPPA compliance flag. Set true for child-directed content (restricts comments, notifications, ad targeting). Defaults to false. YouTube may block views if not explicitly set.
    """
    firstComment: Annotated[str | None, Field(max_length=10000)] = None
    """
    Optional first comment to post immediately after video upload. Up to 10,000 characters (YouTube's comment limit).
    """
    containsSyntheticMedia: bool = False
    """
    AI-generated content disclosure. Set true if the video contains synthetic content that could be mistaken for real. YouTube may add a label.
    """
    categoryId: str = "22"
    """
    YouTube video category ID. Defaults to 22 (People & Blogs). Common: 1 (Film), 2 (Autos), 10 (Music), 15 (Pets), 17 (Sports), 20 (Gaming), 23 (Comedy), 24 (Entertainment), 25 (News), 26 (Howto), 27 (Education), 28 (Science & Tech).
    """


class Type1(Enum):
    """
    Button action type: LEARN_MORE, BOOK, ORDER, SHOP, SIGN_UP, CALL
    """

    LEARN_MORE = "LEARN_MORE"
    BOOK = "BOOK"
    ORDER = "ORDER"
    SHOP = "SHOP"
    SIGN_UP = "SIGN_UP"
    CALL = "CALL"


class CallToAction(BaseModel):
    """
    Optional call-to-action button displayed on the post
    """

    type: Type1
    """
    Button action type: LEARN_MORE, BOOK, ORDER, SHOP, SIGN_UP, CALL
    """
    url: AnyUrl
    """
    Destination URL for the CTA button (required when callToAction is provided)
    """


class GoogleBusinessPlatformData(BaseModel):
    """
    Text and single image only (no videos). Optional call-to-action button. Posts appear on GBP, Google Search, and Maps. Use locationId for multi-location posting.
    """

    locationId: str | None = None
    """
    Target GBP location ID (e.g. "locations/123456789"). If omitted, uses the default location. Use GET /v1/accounts/{id}/gmb-locations to list locations.
    """
    languageCode: Annotated[str | None, Field(examples=["de"])] = None
    """
    BCP 47 language code (e.g. "en", "de", "es"). Auto-detected if omitted. Set explicitly for short or mixed-language posts.
    """
    callToAction: CallToAction | None = None
    """
    Optional call-to-action button displayed on the post
    """


class CommercialContentType(Enum):
    """
    Type of commercial content disclosure
    """

    NONE = "none"
    BRAND_ORGANIC = "brand_organic"
    BRAND_CONTENT = "brand_content"


class MediaType(Enum):
    """
    Optional override. Defaults based on provided media items.
    """

    VIDEO = "video"
    PHOTO = "photo"


class TikTokPlatformData(BaseModel):
    """
    Photo carousels up to 35 images. Video titles up to 2200 chars, photo titles truncated to 90 chars. privacyLevel must match creator_info options. Both camelCase and snake_case accepted.
    """

    draft: bool | None = None
    """
    When true, sends the post to the TikTok Creator Inbox as a draft instead of publishing immediately.
    """
    privacyLevel: str | None = None
    """
    One of the values returned by the TikTok creator info API for the account
    """
    allowComment: bool | None = None
    """
    Allow comments on the post
    """
    allowDuet: bool | None = None
    """
    Allow duets (required for video posts)
    """
    allowStitch: bool | None = None
    """
    Allow stitches (required for video posts)
    """
    commercialContentType: CommercialContentType | None = None
    """
    Type of commercial content disclosure
    """
    brandPartnerPromote: bool | None = None
    """
    Whether the post promotes a brand partner
    """
    isBrandOrganicPost: bool | None = None
    """
    Whether the post is a brand organic post
    """
    contentPreviewConfirmed: bool | None = None
    """
    User has confirmed they previewed the content
    """
    expressConsentGiven: bool | None = None
    """
    User has given express consent for posting
    """
    mediaType: MediaType | None = None
    """
    Optional override. Defaults based on provided media items.
    """
    videoCoverTimestampMs: Annotated[int | None, Field(ge=0)] = None
    """
    Optional for video posts. Timestamp in milliseconds to select which frame to use as thumbnail (defaults to 1000ms/1 second). Ignored when videoCoverImageUrl is provided.
    """
    videoCoverImageUrl: AnyUrl | None = None
    """
    Optional for video posts. URL of a custom thumbnail image (JPG, PNG, or WebP, max 20MB). The image is stitched as a single frame at the start of the video and used as the cover. Overrides videoCoverTimestampMs when provided.
    """
    photoCoverIndex: Annotated[int | None, Field(ge=0)] = None
    """
    Optional for photo carousels. Index of image to use as cover, 0-based (defaults to 0/first image).
    """
    autoAddMusic: bool | None = None
    """
    When true, TikTok may add recommended music (photos only)
    """
    videoMadeWithAi: bool | None = None
    """
    Set true to disclose AI-generated content
    """
    description: Annotated[str | None, Field(max_length=4000)] = None
    """
    Optional long-form description for photo posts (max 4000 chars). Recommended when content exceeds 90 chars, as photo titles are auto-truncated.
    """


class ParseMode(Enum):
    """
    Text formatting mode for the message (default is HTML)
    """

    HTML = "HTML"
    MARKDOWN = "Markdown"
    MARKDOWN_V2 = "MarkdownV2"


class TelegramPlatformData(BaseModel):
    """
    Text, images (up to 10), videos (up to 10), and mixed media albums. Captions up to 1024 chars for media, 4096 for text-only.
    """

    parseMode: ParseMode | None = None
    """
    Text formatting mode for the message (default is HTML)
    """
    disableWebPagePreview: bool | None = None
    """
    Disable link preview generation for URLs in the message
    """
    disableNotification: bool | None = None
    """
    Send the message silently (users will receive notification without sound)
    """
    protectContent: bool | None = None
    """
    Protect message content from forwarding and saving
    """


class ContentType2(Enum):
    """
    Content type: story (ephemeral 24h, default), saved_story (permanent on Public Profile), spotlight (video feed)
    """

    STORY = "story"
    SAVED_STORY = "saved_story"
    SPOTLIGHT = "spotlight"


class SnapchatPlatformData(BaseModel):
    """
    Requires a Public Profile. Single media item only. Content types: story (ephemeral 24h), saved_story (permanent, title max 45 chars), spotlight (video, max 160 chars).
    """

    contentType: ContentType2 = "story"
    """
    Content type: story (ephemeral 24h, default), saved_story (permanent on Public Profile), spotlight (video feed)
    """


class RedditPlatformData(BaseModel):
    """
    Posts are either link (with URL/media) or self (text-only). Use forceSelf to override. Subreddit defaults to the account's configured one. Some subreddits require a flair.
    """

    subreddit: Annotated[str | None, Field(examples=["socialmedia"])] = None
    """
    Target subreddit name (without "r/" prefix). Overrides the default. Use GET /v1/accounts/{id}/reddit-subreddits to list options.
    """
    title: Annotated[str | None, Field(max_length=300)] = None
    """
    Post title. Defaults to the first line of content, truncated to 300 characters.
    """
    url: AnyUrl | None = None
    """
    URL for link posts. If provided (and forceSelf is not true), creates a link post instead of a text post.
    """
    forceSelf: bool | None = None
    """
    When true, creates a text/self post even when a URL or media is provided.
    """
    flairId: Annotated[
        str | None, Field(examples=["a1b2c3d4-e5f6-7890-abcd-ef1234567890"])
    ] = None
    """
    Flair ID for the post. Required by some subreddits. Use GET /v1/accounts/{id}/reddit-flairs?subreddit=name to list flairs.
    """


class BlueskyPlatformData(BaseModel):
    """
    Bluesky post settings. Supports text posts with up to 4 images or a single video. threadItems creates a reply chain (Bluesky thread). Images exceeding 1MB are automatically compressed. Alt text supported via mediaItem properties.

    """

    threadItems: List[ThreadItem] | None = None
    """
    Sequence of posts in a Bluesky thread (root then replies in order).
    """


class QueueSlot(BaseModel):
    dayOfWeek: Annotated[int | None, Field(ge=0, le=6)] = None
    """
    Day of week (0=Sunday, 6=Saturday)
    """
    time: Annotated[str | None, Field(pattern="^([0-1][0-9]|2[0-3]):[0-5][0-9]$")] = (
        None
    )
    """
    Time in HH:mm format (24-hour)
    """


class QueueSchedule(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    """
    Unique queue identifier
    """
    profileId: str | None = None
    """
    Profile ID this queue belongs to
    """
    name: str | None = None
    """
    Queue name (e.g., "Morning Posts", "Evening Content")
    """
    timezone: str | None = None
    """
    IANA timezone (e.g., America/New_York)
    """
    slots: List[QueueSlot] | None = None
    active: bool | None = None
    """
    Whether the queue is active
    """
    isDefault: bool | None = None
    """
    Whether this is the default queue for the profile (used when no queueId specified)
    """
    createdAt: AwareDatetime | None = None
    updatedAt: AwareDatetime | None = None


class Pagination(BaseModel):
    page: int | None = None
    limit: int | None = None
    total: int | None = None
    pages: int | None = None


class Profile(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    userId: str | None = None
    name: str | None = None
    description: str | None = None
    color: str | None = None
    isDefault: bool | None = None
    isOverLimit: bool | None = None
    """
    Only present when includeOverLimit=true. Indicates if this profile exceeds the plan limit.
    """
    createdAt: AwareDatetime | None = None


class SocialAccount(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    platform: str | None = None
    profileId: str | Profile | None = None
    username: str | None = None
    displayName: str | None = None
    profileUrl: str | None = None
    """
    Full profile URL for the connected account on its platform.
    """
    isActive: bool | None = None
    followersCount: float | None = None
    """
    Follower count (only included if user has analytics add-on)
    """
    followersLastUpdated: AwareDatetime | None = None
    """
    Last time follower count was updated (only included if user has analytics add-on)
    """


class AccountStats(BaseModel):
    """
    Platform-specific account stats from the latest daily snapshot.
    Fields vary by platform. Only present if metadata has been captured.

    """

    followingCount: float | None = None
    """
    Number of accounts being followed
    """
    mediaCount: float | None = None
    """
    Total media posts (Instagram)
    """
    videoCount: float | None = None
    """
    Total videos (YouTube
    """
    tweetCount: float | None = None
    """
    Total tweets (X/Twitter)
    """
    postsCount: float | None = None
    """
    Total posts (Bluesky)
    """
    pinCount: float | None = None
    """
    Total pins (Pinterest)
    """
    totalViews: float | None = None
    """
    Total channel views (YouTube)
    """
    likesCount: float | None = None
    """
    Total likes received (TikTok)
    """
    monthlyViews: float | None = None
    """
    Monthly profile views (Pinterest)
    """
    listedCount: float | None = None
    """
    Lists the user appears on (X/Twitter)
    """
    boardCount: float | None = None
    """
    Total boards (Pinterest)
    """


class AccountWithFollowerStats(SocialAccount):
    profilePicture: str | None = None
    currentFollowers: float | None = None
    """
    Current follower count
    """
    lastUpdated: AwareDatetime | None = None
    growth: float | None = None
    """
    Follower change over period
    """
    growthPercentage: float | None = None
    """
    Percentage growth
    """
    dataPoints: float | None = None
    """
    Number of historical snapshots
    """
    accountStats: AccountStats | None = None
    """
    Platform-specific account stats from the latest daily snapshot.
    Fields vary by platform. Only present if metadata has been captured.

    """


class Scope(Enum):
    """
    'full' grants access to all profiles, 'profiles' restricts to specific profiles
    """

    FULL = "full"
    PROFILES = "profiles"


class ProfileId(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    name: str | None = None
    color: str | None = None


class Permission(Enum):
    """
    'read-write' allows all operations, 'read' restricts to GET requests only
    """

    READ_WRITE = "read-write"
    READ = "read"


class ApiKey(BaseModel):
    id: str | None = None
    name: str | None = None
    keyPreview: str | None = None
    expiresAt: AwareDatetime | None = None
    createdAt: AwareDatetime | None = None
    key: str | None = None
    """
    Returned only once, on creation
    """
    scope: Scope = "full"
    """
    'full' grants access to all profiles, 'profiles' restricts to specific profiles
    """
    profileIds: List[ProfileId] | None = None
    """
    Profiles this key can access (populated with name and color). Only present when scope is 'profiles'.
    """
    permission: Permission = "read-write"
    """
    'read-write' allows all operations, 'read' restricts to GET requests only
    """


class BillingPeriod(Enum):
    MONTHLY = "monthly"
    YEARLY = "yearly"


class Limits(BaseModel):
    uploads: int | None = None
    profiles: int | None = None


class Usage(BaseModel):
    uploads: int | None = None
    profiles: int | None = None
    lastReset: AwareDatetime | None = None


class UsageStats(BaseModel):
    planName: str | None = None
    billingPeriod: BillingPeriod | None = None
    signupDate: AwareDatetime | None = None
    billingAnchorDay: int | None = None
    """
    Day of month (1-31) when the billing cycle resets
    """
    limits: Limits | None = None
    usage: Usage | None = None


class PostAnalytics(BaseModel):
    impressions: Annotated[int | None, Field(examples=[0])] = None
    reach: Annotated[int | None, Field(examples=[0])] = None
    likes: Annotated[int | None, Field(examples=[0])] = None
    comments: Annotated[int | None, Field(examples=[0])] = None
    shares: Annotated[int | None, Field(examples=[0])] = None
    saves: Annotated[int | None, Field(examples=[0])] = None
    """
    Number of saves/bookmarks (Instagram, Pinterest)
    """
    clicks: Annotated[int | None, Field(examples=[0])] = None
    views: Annotated[int | None, Field(examples=[0])] = None
    engagementRate: Annotated[float | None, Field(examples=[0])] = None
    lastUpdated: AwareDatetime | None = None


class Status4(Enum):
    PUBLISHED = "published"
    FAILED = "failed"


class Analytics(BaseModel):
    impressions: Annotated[int | None, Field(examples=[0])] = None
    reach: Annotated[int | None, Field(examples=[0])] = None
    likes: Annotated[int | None, Field(examples=[0])] = None
    comments: Annotated[int | None, Field(examples=[0])] = None
    shares: Annotated[int | None, Field(examples=[0])] = None
    saves: Annotated[int | None, Field(examples=[0])] = None
    """
    Number of saves/bookmarks (Instagram, Pinterest)
    """
    clicks: Annotated[int | None, Field(examples=[0])] = None
    views: Annotated[int | None, Field(examples=[0])] = None
    engagementRate: Annotated[float | None, Field(examples=[0])] = None
    lastUpdated: AwareDatetime | None = None


class SyncStatus(Enum):
    """
    Sync state of analytics for this platform
    """

    SYNCED = "synced"
    PENDING = "pending"
    UNAVAILABLE = "unavailable"


class PlatformAnalytics(BaseModel):
    platform: str | None = None
    status: Status4 | None = None
    accountId: str | None = None
    accountUsername: str | None = None
    analytics: Analytics | None = None
    syncStatus: SyncStatus | None = None
    """
    Sync state of analytics for this platform
    """
    platformPostUrl: AnyUrl | None = None
    errorMessage: str | None = None
    """
    Error details when status is failed
    """


class DataStaleness(BaseModel):
    staleAccountCount: int | None = None
    """
    Number of accounts with stale analytics data
    """
    syncTriggered: bool | None = None
    """
    Whether a background sync was triggered for stale accounts
    """


class AnalyticsOverview(BaseModel):
    totalPosts: int | None = None
    publishedPosts: int | None = None
    scheduledPosts: int | None = None
    lastSync: AwareDatetime | None = None
    dataStaleness: DataStaleness | None = None


class Status5(Enum):
    """
    Overall post status. "partial" when some platforms published and others failed.
    """

    PUBLISHED = "published"
    FAILED = "failed"
    PARTIAL = "partial"


class SyncStatus1(Enum):
    """
    Overall sync state across all platforms
    """

    SYNCED = "synced"
    PENDING = "pending"
    PARTIAL = "partial"
    UNAVAILABLE = "unavailable"


class MediaType1(Enum):
    IMAGE = "image"
    VIDEO = "video"
    CAROUSEL = "carousel"
    TEXT = "text"


class Type2(Enum):
    IMAGE = "image"
    VIDEO = "video"


class MediaItem1(BaseModel):
    type: Type2 | None = None
    url: AnyUrl | None = None
    """
    Direct URL to the media
    """
    thumbnail: AnyUrl | None = None
    """
    Thumbnail URL (same as url for images)
    """


class AnalyticsSinglePostResponse(BaseModel):
    postId: str | None = None
    latePostId: str | None = None
    """
    Original Late post ID if scheduled via Late
    """
    status: Status5 | None = None
    """
    Overall post status. "partial" when some platforms published and others failed.
    """
    content: str | None = None
    scheduledFor: AwareDatetime | None = None
    publishedAt: AwareDatetime | None = None
    analytics: PostAnalytics | None = None
    platformAnalytics: List[PlatformAnalytics] | None = None
    platform: str | None = None
    platformPostUrl: AnyUrl | None = None
    isExternal: bool | None = None
    syncStatus: SyncStatus1 | None = None
    """
    Overall sync state across all platforms
    """
    message: str | None = None
    """
    Human-readable status message for pending, partial, or failed states
    """
    thumbnailUrl: AnyUrl | None = None
    mediaType: MediaType1 | None = None
    mediaItems: List[MediaItem1] | None = None
    """
    All media items for this post. Carousel posts contain one entry per slide.
    """


class MediaType2(Enum):
    IMAGE = "image"
    VIDEO = "video"
    GIF = "gif"
    DOCUMENT = "document"
    CAROUSEL = "carousel"
    TEXT = "text"


class MediaItem2(BaseModel):
    type: Type2 | None = None
    url: AnyUrl | None = None
    """
    Direct URL to the media
    """
    thumbnail: AnyUrl | None = None
    """
    Thumbnail URL (same as url for images)
    """


class Post3(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    latePostId: str | None = None
    """
    Original Late post ID if scheduled via Late
    """
    content: str | None = None
    scheduledFor: AwareDatetime | None = None
    publishedAt: AwareDatetime | None = None
    status: str | None = None
    analytics: PostAnalytics | None = None
    platforms: List[PlatformAnalytics] | None = None
    platform: str | None = None
    platformPostUrl: AnyUrl | None = None
    isExternal: bool | None = None
    profileId: str | None = None
    thumbnailUrl: AnyUrl | None = None
    mediaType: MediaType2 | None = None
    mediaItems: List[MediaItem2] | None = None
    """
    All media items for this post. Carousel posts contain one entry per slide.
    """


class AnalyticsListResponse(BaseModel):
    overview: AnalyticsOverview | None = None
    posts: List[Post3] | None = None
    pagination: Pagination | None = None
    accounts: List[SocialAccount] | None = None
    """
    Connected social accounts (followerCount and followersLastUpdated only included if user has analytics add-on)
    """
    hasAnalyticsAccess: bool | None = None
    """
    Whether user has analytics add-on access
    """


class Aggregation(Enum):
    TOTAL = "TOTAL"


class Analytics1(BaseModel):
    impressions: int | None = None
    """
    Total impressions across all posts
    """
    reach: int | None = None
    """
    Unique members reached across all posts
    """
    reactions: int | None = None
    """
    Total reactions across all posts
    """
    comments: int | None = None
    """
    Total comments across all posts
    """
    shares: int | None = None
    """
    Total reshares across all posts
    """
    engagementRate: float | None = None
    """
    Overall engagement rate as percentage
    """


class LinkedInAggregateAnalyticsTotalResponse(BaseModel):
    """
    Response for TOTAL aggregation (lifetime totals)
    """

    accountId: str | None = None
    platform: Annotated[str | None, Field(examples=["linkedin"])] = None
    accountType: Annotated[str | None, Field(examples=["personal"])] = None
    username: str | None = None
    aggregation: Aggregation | None = None
    dateRange: DateRange | None = None
    analytics: Analytics1 | None = None
    note: str | None = None
    lastUpdated: AwareDatetime | None = None


class Aggregation1(Enum):
    DAILY = "DAILY"


class Impression(BaseModel):
    date: date_aliased | None = None
    count: int | None = None


class Reaction(BaseModel):
    date: date_aliased | None = None
    count: int | None = None


class Comment1(BaseModel):
    date: date_aliased | None = None
    count: int | None = None


class Share(BaseModel):
    date: date_aliased | None = None
    count: int | None = None


class Analytics2(BaseModel):
    """
    Daily breakdown of each metric as date/count pairs. Reach not available with DAILY aggregation.
    """

    impressions: List[Impression] | None = None
    reactions: List[Reaction] | None = None
    comments: List[Comment1] | None = None
    shares: List[Share] | None = None


class LinkedInAggregateAnalyticsDailyResponse(BaseModel):
    """
    Response for DAILY aggregation (time series breakdown)
    """

    accountId: str | None = None
    platform: Annotated[str | None, Field(examples=["linkedin"])] = None
    accountType: Annotated[str | None, Field(examples=["personal"])] = None
    username: str | None = None
    aggregation: Aggregation1 | None = None
    dateRange: DateRange | None = None
    analytics: Analytics2 | None = None
    """
    Daily breakdown of each metric as date/count pairs. Reach not available with DAILY aggregation.
    """
    skippedMetrics: List[str] | None = None
    """
    Metrics that were skipped due to API limitations
    """
    note: str | None = None
    lastUpdated: AwareDatetime | None = None


class PostDeleteResponse(BaseModel):
    message: str | None = None


class ProfilesListResponse(BaseModel):
    profiles: List[Profile] | None = None


class ProfileGetResponse(BaseModel):
    profile: Profile | None = None


class ProfileCreateResponse(BaseModel):
    message: str | None = None
    profile: Profile | None = None


class ProfileUpdateResponse(BaseModel):
    message: str | None = None
    profile: Profile | None = None


class ProfileDeleteResponse(BaseModel):
    message: str | None = None


class AccountsListResponse(BaseModel):
    accounts: List[SocialAccount] | None = None
    hasAnalyticsAccess: bool | None = None
    """
    Whether user has analytics add-on access
    """


class AccountGetResponse(BaseModel):
    account: SocialAccount | None = None


class DateRange3(BaseModel):
    from_: Annotated[AwareDatetime | None, Field(alias="from")] = None
    to: AwareDatetime | None = None


class Aggregation2(Enum):
    DAILY = "daily"
    WEEKLY = "weekly"
    MONTHLY = "monthly"


class FollowerStatsResponse(BaseModel):
    accounts: List[AccountWithFollowerStats] | None = None
    dateRange: DateRange3 | None = None
    aggregation: Aggregation2 | None = None


class Type4(Enum):
    IMAGE = "image"
    VIDEO = "video"
    DOCUMENT = "document"


class UploadedFile(BaseModel):
    type: Type4 | None = None
    url: AnyUrl | None = None
    filename: str | None = None
    size: int | None = None
    mimeType: str | None = None


class MediaUploadResponse(BaseModel):
    files: List[UploadedFile] | None = None


class Status6(Enum):
    PENDING = "pending"
    COMPLETED = "completed"
    EXPIRED = "expired"


class UploadTokenResponse(BaseModel):
    token: str | None = None
    uploadUrl: AnyUrl | None = None
    expiresAt: AwareDatetime | None = None
    status: Status6 | None = None


class UploadTokenStatusResponse(BaseModel):
    token: str | None = None
    status: Status6 | None = None
    files: List[UploadedFile] | None = None
    createdAt: AwareDatetime | None = None
    expiresAt: AwareDatetime | None = None
    completedAt: AwareDatetime | None = None


class QueueSlotsResponse(BaseModel):
    exists: bool | None = None
    schedule: QueueSchedule | None = None
    nextSlots: List[AwareDatetime] | None = None


class QueueUpdateResponse(BaseModel):
    success: bool | None = None
    schedule: QueueSchedule | None = None
    nextSlots: List[AwareDatetime] | None = None
    reshuffledCount: int | None = None


class QueueDeleteResponse(BaseModel):
    success: bool | None = None
    deleted: bool | None = None


class QueuePreviewResponse(BaseModel):
    profileId: str | None = None
    count: int | None = None
    slots: List[AwareDatetime] | None = None


class QueueNextSlotResponse(BaseModel):
    profileId: str | None = None
    nextSlot: AwareDatetime | None = None
    timezone: str | None = None


class DownloadFormat(BaseModel):
    formatId: str | None = None
    ext: str | None = None
    resolution: str | None = None
    filesize: int | None = None
    quality: str | None = None


class DownloadResponse(BaseModel):
    url: AnyUrl | None = None
    title: str | None = None
    thumbnail: AnyUrl | None = None
    duration: int | None = None
    formats: List[DownloadFormat] | None = None


class TranscriptSegment(BaseModel):
    text: str | None = None
    start: float | None = None
    duration: float | None = None


class TranscriptResponse(BaseModel):
    transcript: str | None = None
    segments: List[TranscriptSegment] | None = None
    language: str | None = None


class Status8(Enum):
    SAFE = "safe"
    BANNED = "banned"
    RESTRICTED = "restricted"
    UNKNOWN = "unknown"


class HashtagInfo(BaseModel):
    hashtag: str | None = None
    status: Status8 | None = None
    postCount: int | None = None


class HashtagCheckResponse(BaseModel):
    hashtags: List[HashtagInfo] | None = None


class CaptionResponse(BaseModel):
    caption: str | None = None


class User(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    email: str | None = None
    name: str | None = None
    role: str | None = None
    createdAt: AwareDatetime | None = None


class UsersListResponse(BaseModel):
    users: List[User] | None = None


class UserGetResponse(BaseModel):
    user: User | None = None


class PlatformTarget(BaseModel):
    platform: Annotated[str | None, Field(examples=["twitter"])] = None
    """
    Supported values: twitter, threads, instagram, youtube, facebook, linkedin, pinterest, reddit, tiktok, bluesky, googlebusiness, telegram
    """
    accountId: str | SocialAccount | None = None
    customContent: str | None = None
    """
    Platform-specific text override. When set, this content is used instead of the top-level post content for this platform. Useful for tailoring captions per platform (e.g. keeping tweets under 280 characters).
    """
    customMedia: List[MediaItem] | None = None
    scheduledFor: AwareDatetime | None = None
    """
    Optional per-platform scheduled time override (uses post.scheduledFor when omitted)
    """
    platformSpecificData: (
        TwitterPlatformData
        | ThreadsPlatformData
        | FacebookPlatformData
        | InstagramPlatformData
        | LinkedInPlatformData
        | PinterestPlatformData
        | YouTubePlatformData
        | GoogleBusinessPlatformData
        | TikTokPlatformData
        | TelegramPlatformData
        | SnapchatPlatformData
        | RedditPlatformData
        | BlueskyPlatformData
        | None
    ) = None
    """
    Platform-specific overrides and options.
    """
    status: Annotated[str | None, Field(examples=["pending"])] = None
    """
    Platform-specific status: pending, publishing, published, failed
    """
    platformPostId: Annotated[str | None, Field(examples=["1234567890123456789"])] = (
        None
    )
    """
    The native post ID on the platform (populated after successful publish)
    """
    platformPostUrl: Annotated[
        AnyUrl | None,
        Field(examples=["https://twitter.com/acmecorp/status/1234567890123456789"]),
    ] = None
    """
    Public URL of the published post. Included in the response for immediate posts; for scheduled posts, fetch via GET /v1/posts/{postId} after publish time.
    """
    publishedAt: AwareDatetime | None = None
    """
    Timestamp when the post was published to this platform
    """
    errorMessage: str | None = None
    """
    Human-readable error message when status is failed. Contains platform-specific error details explaining why the publish failed.
    """
    errorCategory: ErrorCategory | None = None
    """
    Error category for programmatic handling: auth_expired (token expired/revoked), user_content (wrong format/too long), user_abuse (rate limits/spam), account_issue (config problems), platform_rejected (policy violation), platform_error (5xx/maintenance), system_error (Zernio infra), unknown
    """
    errorSource: ErrorSource | None = None
    """
    Who caused the error: user (fix content/reconnect), platform (outage/API change), system (Zernio issue, rare)
    """


class Post2(BaseModel):
    field_id: Annotated[str | None, Field(alias="_id")] = None
    userId: str | User | None = None
    title: str | None = None
    """
    YouTube: title must be ≤ 100 characters.

    """
    content: str | None = None
    mediaItems: List[MediaItem] | None = None
    platforms: List[PlatformTarget] | None = None
    scheduledFor: AwareDatetime | None = None
    timezone: str | None = None
    status: Status3 | None = None
    tags: List[str] | None = None
    """
    YouTube constraints: each tag max 100 chars, combined max 500 chars, duplicates removed.
    """
    hashtags: List[str] | None = None
    mentions: List[str] | None = None
    visibility: Visibility | None = None
    metadata: Dict[str, Any] | None = None
    recycling: RecyclingState | None = None
    recycledFromPostId: str | None = None
    """
    ID of the original post if this post was created via recycling
    """
    queuedFromProfile: str | None = None
    """
    Profile ID if the post was scheduled via the queue
    """
    queueId: str | None = None
    """
    Queue ID if the post was scheduled via a specific queue
    """
    createdAt: AwareDatetime | None = None
    updatedAt: AwareDatetime | None = None


class PostsListResponse(BaseModel):
    posts: List[Post2] | None = None
    pagination: Pagination | None = None


class PostGetResponse(BaseModel):
    post: Post2 | None = None


class PostCreateResponse(BaseModel):
    message: str | None = None
    post: Post2 | None = None


class PostUpdateResponse(BaseModel):
    message: str | None = None
    post: Post2 | None = None


class PostRetryResponse(BaseModel):
    message: str | None = None
    post: Post2 | None = None
