{"openapi":"3.0.0","paths":{"/auth/request-link":{"post":{"description":"Creates the user if missing, then issues a 15-minute magic link. In development, the link is logged to the server console. Rate-limited to 5 per minute per IP to prevent mail bombing.","operationId":"AuthController_requestLink","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestLinkDto"}}}},"responses":{"200":{"description":"Link queued for delivery"},"429":{"description":"Rate limit exceeded"}},"summary":"Send a magic-link to the given email","tags":["auth"]}},"/auth/consume":{"post":{"description":"Tokens are single-use and expire after 15 minutes. The returned sessionKey is a tk_*-prefixed API key with full scope; store it client-side as a Bearer token.","operationId":"AuthController_consume","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeLinkDto"}}}},"responses":{"200":{"description":"Session key issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeResponseDto"}}}},"401":{"description":"Invalid or expired token"}},"summary":"Exchange a magic-link token for a session API key","tags":["auth"]}},"/auth/me":{"get":{"operationId":"AuthController_me","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeDto"}}}},"401":{"description":"Missing or invalid API key"}},"security":[{"apiKey":[]}],"summary":"Return the user the bearer key belongs to","tags":["auth"]}},"/auth/api-keys":{"get":{"operationId":"AuthController_listApiKeys","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeySummaryDto"}}}}}},"security":[{"apiKey":[]}],"summary":"List your API keys","tags":["auth"]},"post":{"description":"The plain key is returned once and never retrievable afterwards. Pass `scopes` to restrict the key (e.g. [\"read\"] for read-only). Omit or pass [\"*\"] for full access.","operationId":"AuthController_createApiKey","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedApiKeyDto"}}}}},"security":[{"apiKey":[]}],"summary":"Create a new API key","tags":["auth"]}},"/auth/api-keys/{id}":{"delete":{"operationId":"AuthController_revokeApiKey","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Key revoked"},"400":{"description":"Key not found or already revoked"}},"security":[{"apiKey":[]}],"summary":"Revoke an API key","tags":["auth"]}},"/api/v1/me/summary":{"get":{"description":"Returns the caller's account counts, user-scoped queue depth across all action types, and the number of actions that succeeded in the last 24 hours.","operationId":"AccountsController_getSummary","parameters":[],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Per-user dashboard summary","tags":["Accounts","accounts"]}},"/api/v1/accounts":{"get":{"operationId":"AccountsController_listAccounts","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountsResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"List your connected X accounts","tags":["Accounts","accounts"]}},"/api/v1/accounts/cookie-validate":{"post":{"description":"Probes X with the candidate auth_token + ct0 (and optional twid) cookies to verify the session is alive and resolves the screen_name. Use this before POST /accounts/:id so the panel can warn instead of saving stale credentials. Stateless — nothing is persisted.","operationId":"AccountsController_validateCookies","parameters":[],"responses":{"200":{"description":"Probe result. ok=true means cookies authenticate; ok=false includes a reason."}},"security":[{"apiKey":[]}],"summary":"Pre-flight cookie health-check before manual paste","tags":["Accounts","accounts"]}},"/api/v1/accounts/{id}/refresh-profile":{"post":{"operationId":"AccountsController_refreshProfile","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Refresh cached profile data for an account","tags":["Accounts","accounts"]}},"/api/v1/accounts/{id}":{"delete":{"description":"Deletes the account, its monitors (cascaded), and clears related session/content state. Pending and failed actions are cancelled; succeeded/dead rows are kept for audit.","operationId":"AccountsController_deleteAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Account deleted"},"404":{"description":"Account not found or not yours"}},"security":[{"apiKey":[]}],"summary":"Disconnect an X account","tags":["Accounts","accounts"]},"put":{"description":"Token-paste connect: provide authToken/ct0/twid copied from a logged-in browser session. Empty fields preserve existing values on update. Rate-limited to 3 calls per 15 minutes (stricter than the default write tier to discourage credential brute force).","operationId":"AccountsController_upsertAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountUpsertDto"}}}},"responses":{"200":{"description":"Account upserted"},"400":{"description":"Validation error or account belongs to another user"},"429":{"description":"Rate limit exceeded"}},"security":[{"apiKey":[]}],"summary":"Connect or update an X account","tags":["Accounts","accounts"]}},"/api/v1/actions/post":{"post":{"operationId":"ActionsController_enqueuePost","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostActionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a tweet","tags":["Actions","actions"]}},"/api/v1/actions/reply":{"post":{"operationId":"ActionsController_enqueueReply","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyActionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a reply","tags":["Actions","actions"]}},"/api/v1/actions/like":{"post":{"operationId":"ActionsController_enqueueLike","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a like","tags":["Actions","actions"]}},"/api/v1/actions/retweet":{"post":{"operationId":"ActionsController_enqueueRetweet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a retweet","tags":["Actions","actions"]}},"/api/v1/actions/quote":{"post":{"operationId":"ActionsController_enqueueQuote","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteActionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a quote tweet","tags":["Actions","actions"]}},"/api/v1/actions/bookmark":{"post":{"operationId":"ActionsController_enqueueBookmark","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}}},"security":[{"apiKey":[]}],"summary":"Enqueue a bookmark","tags":["Actions","actions"]}},"/api/v1/actions/follow":{"post":{"description":"Rate-limited to 20 follows per minute and 400 per day per user.","operationId":"ActionsController_enqueueFollow","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowBody"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionEnqueueResponseDto"}}}},"429":{"description":"Rate limit exceeded"}},"security":[{"apiKey":[]}],"summary":"Enqueue a follow","tags":["Actions","actions"]}},"/api/v1/actions/thread":{"post":{"operationId":"ActionsController_enqueueThread","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Enqueue a multi-tweet thread","tags":["Actions","actions"]}},"/api/v1/actions":{"get":{"operationId":"ActionsController_listActions","parameters":[{"name":"type","required":true,"in":"query","schema":{"enum":["post","reply","retweet","like","follow","quote","bookmark","unlike","unretweet","unfollow","delete_tweet","dm","profile_update","avatar_update","banner_update"],"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"List your actions filtered by type/status/account","tags":["Actions","actions"]}},"/api/v1/actions/{type}/{id}/cancel":{"post":{"operationId":"ActionsController_cancelAction","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Cancel a pending action","tags":["Actions","actions"]}},"/api/v1/actions/{type}/{id}/replay":{"post":{"operationId":"ActionsController_replayAction","parameters":[{"name":"type","required":true,"in":"path","schema":{"type":"string"}},{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Replay a failed or dead action","tags":["Actions","actions"]}},"/api/v1/accounts/connect":{"post":{"description":"Queues a headless login job. The browser logs in to x.com with the provided credentials, extracts the session cookies, and stores them as a new connected account. The response returns immediately with a job id; poll GET /accounts/login-jobs/:jobId every 2s. Typical end-to-end duration is 20–40s. Rate-limited to 3 calls per 15 minutes per user.","operationId":"LoginController_connectAccount","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountConnectDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginJobAcceptedDto"}}}},"400":{"description":"Validation error"},"429":{"description":"Rate limit exceeded"}},"security":[{"apiKey":[]}],"summary":"Connect a new X account via server-side login","tags":["Login","accounts"]}},"/api/v1/accounts/login-jobs/{jobId}":{"get":{"description":"Returns the current state of a connect/reauth job you own. Encrypted credentials are never exposed.","operationId":"LoginController_getLoginJob","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginJobResponseDto"}}}},"404":{"description":"Job not found or not yours"}},"security":[{"apiKey":[]}],"summary":"Poll a login job","tags":["Login","accounts"]},"delete":{"description":"Flips a queued or running login job to status=cancelled. For a running job the worker observes the flip between steps and unwinds the Patchright session — typical latency 1–5s, never more than one step. Already-terminal jobs (success/failed/cancelled) return 409.","operationId":"LoginController_cancelLoginJob","parameters":[{"name":"jobId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"Cancellation accepted; priorStatus indicates whether the worker had picked it up."},"404":{"description":"Job not found or not yours"},"409":{"description":"Job is already terminal"}},"security":[{"apiKey":[]}],"summary":"Cancel a login job","tags":["Login","accounts"]}},"/api/v1/accounts/{id}/reauth":{"post":{"description":"Use when a connected account becomes unhealthy (session expired, paused after auth failures). Provides fresh credentials for a server-side login that overwrites cookies on the existing account row. The handle of the logged-in session must match the target account; otherwise the job fails with invalid_credentials.","operationId":"LoginController_reauthAccount","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountReauthDto"}}}},"responses":{"202":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginJobAcceptedDto"}}}},"404":{"description":"Account not found or not yours"}},"security":[{"apiKey":[]}],"summary":"Re-authenticate an existing X account","tags":["Login","accounts"]}},"/api/v1/monitors":{"get":{"operationId":"MonitorsController_listMonitors","parameters":[],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"List your monitors","tags":["Monitors","monitors"]},"post":{"description":"Returns a `webhookSecret` on creation **once**. Use it to verify the X-Tweetly-Signature header on incoming webhook deliveries. The secret is never returned again — store it server-side. Use POST /monitors/:id/rotate-secret if you lose it or need to rotate.","operationId":"MonitorsController_createMonitor","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonitorCreateDto"}}}},"responses":{"201":{"description":""}},"security":[{"apiKey":[]}],"summary":"Create a monitor with webhook delivery","tags":["Monitors","monitors"]}},"/api/v1/monitors/{id}":{"get":{"operationId":"MonitorsController_getMonitor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get monitor + recent webhook deliveries","tags":["Monitors","monitors"]},"delete":{"operationId":"MonitorsController_deleteMonitor","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Delete a monitor","tags":["Monitors","monitors"]}},"/api/v1/monitors/{id}/rotate-secret":{"post":{"description":"Returns the new secret once; the old one immediately stops being valid.","operationId":"MonitorsController_rotateMonitorSecret","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Rotate the webhook signing secret","tags":["Monitors","monitors"]}},"/api/v1/x/search/tweets":{"get":{"operationId":"XController_searchTweets","parameters":[{"name":"query","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Search tweets matching a query (live)","tags":["X","x"]}},"/api/v1/x/search/users":{"get":{"operationId":"XController_searchUsers","parameters":[{"name":"query","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"account","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Search users by name or handle","tags":["X","x"]}},"/api/v1/x/users/{handle}":{"get":{"operationId":"XController_getUser","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"account","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get a user profile","tags":["X","x"]}},"/api/v1/x/users/{handle}/tweets":{"get":{"operationId":"XController_getUserTweets","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"account","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get a user's recent tweets","tags":["X","x"]}},"/api/v1/x/users/{handle}/followers":{"get":{"operationId":"XController_getUserFollowers","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"account","required":true,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get a user's followers","tags":["X","x"]}},"/api/v1/x/tweets/get":{"post":{"operationId":"XController_getTweet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTweetBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get tweet details by URL","tags":["X","x"]}},"/api/v1/x/trending":{"get":{"operationId":"XController_getXTrending","parameters":[{"name":"account","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get current X trending topics","tags":["X","x"]}},"/api/v1/x/users/{handle}/likes":{"get":{"operationId":"XController_getUserLikes","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get tweets a user has liked","tags":["X","x"]}},"/api/v1/x/me/bookmarks":{"get":{"operationId":"XController_getMyBookmarks","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get the calling account's own bookmarks","tags":["X","x"]}},"/api/v1/x/lists/{listId}/members":{"get":{"operationId":"XController_getListMembers","parameters":[{"name":"listId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get members of a public X list by numeric ID","tags":["X","x"]}},"/api/v1/x/users/{handle}/mutual-followers":{"get":{"operationId":"XController_getMutualFollowers","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Followers-you-know: accounts the calling user follows that also follow :handle","tags":["X","x"]}},"/api/v1/x/users/{handle}/lists":{"get":{"operationId":"XController_getUserLists","parameters":[{"name":"handle","required":true,"in":"path","schema":{"type":"string"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get the lists a user owns","tags":["X","x"]}},"/api/v1/x/lists/{listId}":{"get":{"operationId":"XController_getList","parameters":[{"name":"listId","required":true,"in":"path","schema":{"type":"string"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get list metadata (name, description, member + subscriber counts, owner)","tags":["X","x"]}},"/api/v1/x/lists/{listId}/subscribers":{"get":{"operationId":"XController_getListSubscribers","parameters":[{"name":"listId","required":true,"in":"path","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}},{"name":"account","required":false,"in":"query","schema":{"type":"string"}},{"name":"cursor","required":false,"in":"query","description":"Opaque cursor from a previous nextCursor","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get subscribers of a public X list (paginated)","tags":["X","x"]}},"/api/v1/x/tweets/thread":{"post":{"operationId":"XController_getThread","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTweetBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get the same-author thread chain rooted at a tweet (root tweet first)","tags":["X","x"]}},"/api/v1/x/tweets/unlike":{"post":{"operationId":"XController_unlikeTweet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Remove a like (synchronous)","tags":["X","x"]}},"/api/v1/x/tweets/unretweet":{"post":{"operationId":"XController_unretweet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Undo a retweet (synchronous)","tags":["X","x"]}},"/api/v1/x/tweets/delete":{"post":{"operationId":"XController_deleteTweet","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InteractionBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Delete a tweet (synchronous)","tags":["X","x"]}},"/api/v1/x/follows/unfollow":{"post":{"operationId":"XController_unfollow","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Unfollow an account (synchronous)","tags":["X","x"]}},"/api/v1/x/dm/send":{"post":{"operationId":"XController_sendDm","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendDmBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Send a direct message","tags":["X","x"]}},"/api/v1/x/profile":{"put":{"operationId":"XController_updateProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileBody"}}}},"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Update profile fields (name/bio/location/website)","tags":["X","x"]}},"/api/v1/extractions":{"post":{"operationId":"ExtractionsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["type","params"],"properties":{"type":{"type":"string","enum":["user_followers","user_following","user_tweets","user_likes","user_mentions","tweet_retweeters","search_tweets","list_members"]},"params":{"type":"object","properties":{"handle":{"type":"string"},"tweetUrl":{"type":"string"},"listId":{"type":"string"},"query":{"type":"string"},"verifiedOnly":{"type":"boolean"}}},"max_rows":{"type":"integer","minimum":1,"maximum":100000,"default":1000},"account":{"type":"string","nullable":true}}}}}},"responses":{"202":{"description":""}},"security":[{"apiKey":[]}],"summary":"Queue a bulk extraction job (async). Polls one of the cursor-paginated read endpoints under the hood and writes results as JSONL.","tags":["extractions"]},"get":{"operationId":"ExtractionsController_list","parameters":[{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"List the caller's recent extraction jobs","tags":["extractions"]}},"/api/v1/extractions/{id}":{"get":{"operationId":"ExtractionsController_get","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Get extraction job status + metadata","tags":["extractions"]},"delete":{"operationId":"ExtractionsController_cancel","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"apiKey":[]}],"summary":"Cancel a queued or running extraction job","tags":["extractions"]}},"/api/v1/extractions/{id}/download":{"get":{"operationId":"ExtractionsController_download","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"apiKey":[]}],"summary":"Stream the extraction result file (JSONL: one item per line)","tags":["extractions"]}},"/copilot/analyze-profile":{"post":{"operationId":"AiCopilotController_analyzeProfile","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeProfileDto"}}}},"responses":{"201":{"description":""}},"tags":["AiCopilot"]}},"/copilot/suggest":{"post":{"operationId":"AiCopilotController_suggest","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSuggestDto"}}}},"responses":{"201":{"description":""}},"tags":["AiCopilot"]}},"/copilot/score":{"post":{"operationId":"AiCopilotController_score","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViralScoreDto"}}}},"responses":{"201":{"description":""}},"tags":["AiCopilot"]}},"/copilot/publish":{"post":{"operationId":"AiCopilotController_publish","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishTweetDto"}}}},"responses":{"201":{"description":""}},"tags":["AiCopilot"]}},"/copilot/formats":{"get":{"operationId":"AiCopilotController_getFormats","parameters":[],"responses":{"200":{"description":""}},"tags":["AiCopilot"]}},"/copilot/history":{"get":{"operationId":"AiCopilotController_getHistory","parameters":[{"name":"type","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["AiCopilot"]}},"/agent/configs":{"get":{"operationId":"AgentController_listConfigs","parameters":[],"responses":{"200":{"description":""}},"tags":["Agent"]},"post":{"operationId":"AgentController_createConfig","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentConfigDto"}}}},"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/configs/{id}":{"patch":{"operationId":"AgentController_updateConfig","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAgentConfigDto"}}}},"responses":{"200":{"description":""}},"tags":["Agent"]},"delete":{"operationId":"AgentController_deleteConfig","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Agent"]}},"/agent/style-profile/{accountId}":{"get":{"operationId":"AgentController_getStyleProfile","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Agent"]},"post":{"operationId":"AgentController_updateStyleProfile","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStyleProfileDto"}}}},"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/style-profile/{accountId}/analyze":{"post":{"operationId":"AgentController_analyzeStyleProfile","parameters":[{"name":"accountId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeStyleDto"}}}},"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/drafts":{"get":{"operationId":"AgentController_listDrafts","parameters":[{"name":"status","required":true,"in":"query","schema":{"type":"string"}},{"name":"accountId","required":true,"in":"query","schema":{"type":"string"}},{"name":"limit","required":true,"in":"query","schema":{"type":"string"}},{"name":"offset","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"tags":["Agent"]}},"/agent/drafts/stats":{"get":{"operationId":"AgentController_getDraftStats","parameters":[],"responses":{"200":{"description":""}},"tags":["Agent"]}},"/agent/drafts/{id}/approve":{"post":{"operationId":"AgentController_approveDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveDraftDto"}}}},"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/drafts/{id}/reject":{"post":{"operationId":"AgentController_rejectDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/drafts/{id}":{"patch":{"operationId":"AgentController_editDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditDraftDto"}}}},"responses":{"200":{"description":""}},"tags":["Agent"]}},"/agent/drafts/{id}/edit-and-approve":{"post":{"operationId":"AgentController_editAndApproveDraft","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditAndApproveDraftDto"}}}},"responses":{"201":{"description":""}},"tags":["Agent"]}},"/agent/trigger/{configId}":{"post":{"operationId":"AgentController_triggerAgent","parameters":[{"name":"configId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"tags":["Agent"]}}},"info":{"title":"xtweetly API","description":"Multi-tenant X (Twitter) automation platform. Connect your X accounts and run actions (post, reply, like, retweet, quote, follow, bookmark, search, monitor) from your own AI agents over MCP or REST.","version":"1.0.0","contact":{}},"tags":[{"name":"auth","description":"Magic-link login and API key management"},{"name":"accounts","description":"Connected X accounts"},{"name":"actions","description":"Asynchronous X actions (post, reply, like, ...)"},{"name":"x","description":"Direct X read/undo operations (synchronous via Patchright)"},{"name":"monitors","description":"Account monitors with webhook delivery"}],"servers":[],"components":{"securitySchemes":{"apiKey":{"scheme":"bearer","bearerFormat":"tk_*","type":"http","description":"xtweetly API key issued from /auth/api-keys"}},"schemas":{"RequestLinkDto":{"type":"object","properties":{"email":{"type":"string","example":"you@example.com","description":"Account email address"}},"required":["email"]},"ConsumeLinkDto":{"type":"object","properties":{"token":{"type":"string","example":"a3e4f385cc761137690bb42f4c0e34e9f6eaad9c082037ea172d190cb8fbc718","description":"Magic-link token sent to the user email"}},"required":["token"]},"ConsumeResponseDto":{"type":"object","properties":{"ok":{"type":"boolean"},"sessionKey":{"type":"string","example":"tk_xxx...","description":"Session API key — store and use as Bearer token"},"user":{"type":"object","example":{"id":"a8765905-...","email":"you@example.com"}}},"required":["ok","sessionKey","user"]},"MeDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","example":"you@example.com"},"status":{"type":"string","enum":["active","suspended"]}},"required":["id","email","status"]},"ApiKeySummaryDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"prefix":{"type":"string","example":"tk_0e97c65e"},"scopes":{"type":"array","items":{"type":"string"}},"lastUsedAt":{"type":"string","nullable":true},"expiresAt":{"type":"string","nullable":true},"createdAt":{"type":"string"},"revokedAt":{"type":"string","nullable":true},"issuedVia":{"type":"string","enum":["manual","oauth"]},"oauthClientId":{"type":"string","nullable":true,"description":"Set when issuedVia is \"oauth\""}},"required":["id","name","prefix","scopes","lastUsedAt","expiresAt","createdAt","revokedAt","issuedVia","oauthClientId"]},"CreateApiKeyDto":{"type":"object","properties":{"name":{"type":"string","example":"Claude Code","description":"Human-readable label for the key"},"scopes":{"example":["*"],"description":"Reserved for future scope enforcement (default: [\"*\"])","type":"array","items":{"type":"string"}}},"required":["name"]},"CreatedApiKeyDto":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"key":{"type":"string","example":"tk_xxx...","description":"Plain key — shown only once. Store securely."},"prefix":{"type":"string"},"name":{"type":"string"}},"required":["id","key","prefix","name"]},"SessionHealthDto":{"type":"object","properties":{"health":{"type":"string","enum":["unknown","healthy","unhealthy"]},"lastCheckAt":{"type":"string","nullable":true},"lastFailureAt":{"type":"string","nullable":true},"lastFailureReason":{"type":"string","nullable":true},"authFailureCount":{"type":"number","description":"Consecutive auth failures since last success"}},"required":["health","lastCheckAt","lastFailureAt","lastFailureReason","authFailureCount"]},"AccountProfileDto":{"type":"object","properties":{"displayName":{"type":"string"},"bio":{"type":"string"},"followersCount":{"type":"string"},"followingCount":{"type":"string"},"tweetsCount":{"type":"string"},"profileImageUrl":{"type":"string"},"verified":{"type":"boolean"},"fetchedAt":{"format":"date-time","type":"string"}},"required":["displayName","bio","followersCount","followingCount","tweetsCount","profileImageUrl","verified","fetchedAt"]},"RedactedAccountDto":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string","nullable":true},"status":{"type":"string","enum":["active","paused","banned"]},"hasAuthToken":{"type":"boolean"},"hasAuthMulti":{"type":"boolean"},"hasCt0":{"type":"boolean"},"hasTwid":{"type":"boolean"},"createdAt":{"format":"date-time","type":"string"},"lastUsedAt":{"format":"date-time","type":"string","nullable":true},"session":{"$ref":"#/components/schemas/SessionHealthDto"},"profile":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/AccountProfileDto"}]}},"required":["id","displayName","status","hasAuthToken","hasAuthMulti","hasCt0","hasTwid","createdAt","lastUsedAt","session","profile"]},"AccountsResponseDto":{"type":"object","properties":{"count":{"type":"number"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/RedactedAccountDto"}}},"required":["count","accounts"]},"AccountUpsertDto":{"type":"object","properties":{"displayName":{"type":"object","nullable":true,"example":"Alice X"},"authToken":{"type":"string","description":"X session auth_token cookie"},"authMulti":{"type":"object","nullable":true,"description":"X auth_multi cookie (multi-account session)"},"ct0":{"type":"object","nullable":true,"description":"X CSRF cookie (ct0)"},"twid":{"type":"object","nullable":true,"description":"X user identifier cookie (twid)"},"status":{"type":"string","enum":["active","paused","banned"]}}},"PostActionBody":{"type":"object","properties":{"text":{"type":"string","example":"Hello from xtweetly","description":"Tweet text (max 280 chars)"},"account":{"type":"string","description":"Account ID to post from (uses first active account if omitted)"}},"required":["text"]},"ActionEnqueueResponseDto":{"type":"object","properties":{"id":{"type":"object","format":"uuid","nullable":true,"description":"Null when deduped to an existing pending row"},"idempotencyKey":{"type":"string","description":"Deterministic dedup key for this action"}},"required":["id","idempotencyKey"]},"ReplyActionBody":{"type":"object","properties":{"text":{"type":"string","example":"Nice take!"},"parentTweetUrl":{"type":"string","example":"https://x.com/user/status/123","description":"Must contain /status/"},"account":{"type":"string"}},"required":["text","parentTweetUrl"]},"InteractionBody":{"type":"object","properties":{"targetTweetUrl":{"type":"string","description":"Must contain /status/"},"account":{"type":"string"}},"required":["targetTweetUrl"]},"QuoteActionBody":{"type":"object","properties":{"text":{"type":"string"},"targetTweetUrl":{"type":"string","description":"Must contain /status/"},"account":{"type":"string"}},"required":["text","targetTweetUrl"]},"FollowBody":{"type":"object","properties":{"targetHandle":{"type":"string","example":"jack","description":"Handle without @"},"account":{"type":"string"}},"required":["targetHandle"]},"ThreadBody":{"type":"object","properties":{"tweets":{"example":["first tweet","second tweet"],"type":"array","items":{"type":"string"}},"account":{"type":"string"}},"required":["tweets"]},"AccountConnectDto":{"type":"object","properties":{"username":{"type":"string","description":"X handle. Leading @ is stripped automatically.","example":"alice"},"email":{"type":"object","nullable":true,"description":"Email tied to the X account. Optional unless X asks for an unusual-login challenge."},"password":{"type":"string","description":"X account password. Encrypted at rest immediately."},"totpSecret":{"type":"object","nullable":true,"description":"Base32 TOTP secret (NOT the 6-digit code). Find it in X → Settings → Security → 2FA → Authenticator app → 'Can't scan QR?'. Required for 2FA-enabled accounts."},"saveTotpSecret":{"type":"boolean","default":false,"description":"When true, the encrypted TOTP secret is retained on the account so re-auth can run without re-prompting. When false, the secret is wiped after the login job completes."},"proxyCountry":{"type":"object","nullable":true,"example":"TR","description":"Optional 2-letter proxy country code for the login browser. If omitted, LOGIN_DEFAULT_PROXY_COUNTRY is used when configured."}},"required":["username","password"]},"LoginJobAcceptedDto":{"type":"object","properties":{"jobId":{"type":"string"},"kind":{"type":"string","enum":["connect","reauth"]},"pollUrl":{"type":"string","description":"Suggested polling URL for status updates.","example":"/api/v1/accounts/login-jobs/<id>"}},"required":["jobId","kind","pollUrl"]},"LoginJobResponseDto":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["connect","reauth"]},"status":{"type":"string","enum":["queued","running","success","failed","cancelled"]},"targetAccountId":{"type":"string","nullable":true,"description":"Set once the login completes (handle of the connected account)."},"failureReason":{"type":"string","nullable":true,"enum":["invalid_credentials","captcha_required","email_challenge","email_verification_required","suspicious_login_blocked","login_cooldown","cookies_missing","home_not_reached","account_locked","phone_verification_required","cancelled","unknown"]},"failureDetail":{"type":"string","nullable":true},"createdAt":{"type":"string"},"startedAt":{"type":"string","nullable":true},"finishedAt":{"type":"string","nullable":true}},"required":["id","kind","status","targetAccountId","failureReason","failureDetail","createdAt","startedAt","finishedAt"]},"AccountReauthDto":{"type":"object","properties":{"password":{"type":"string","description":"Current X account password."},"totpSecret":{"type":"object","nullable":true,"description":"Base32 TOTP secret. Required if 2FA is enabled and not stored on the account."},"saveTotpSecret":{"type":"boolean","default":false},"email":{"type":"object","nullable":true,"description":"Update the stored email during reauth."},"proxyCountry":{"type":"object","nullable":true,"example":"TR","description":"Optional 2-letter proxy country code for this reauth job. Defaults to the account proxy country, then LOGIN_DEFAULT_PROXY_COUNTRY."}},"required":["password"]},"MonitorCreateDto":{"type":"object","properties":{"targetHandle":{"type":"string","example":"jack","description":"Handle to monitor (without @)"},"webhookUrl":{"type":"string","example":"https://your-host/webhook","description":"HTTPS URL to POST events to"},"accountId":{"type":"string","description":"Account to use for polling (defaults to first active)"},"eventTypes":{"type":"array","example":["tweet.new"],"items":{"type":"string","enum":["tweet.new"]}}},"required":["targetHandle","webhookUrl"]},"GetTweetBody":{"type":"object","properties":{"tweetUrl":{"type":"string","description":"Must contain /status/"},"account":{"type":"string"}},"required":["tweetUrl"]},"SendDmBody":{"type":"object","properties":{"targetHandle":{"type":"string","description":"Recipient handle (without @)"},"message":{"type":"string","description":"Message text"},"account":{"type":"string"}},"required":["targetHandle","message"]},"UpdateProfileBody":{"type":"object","properties":{"name":{"type":"string"},"bio":{"type":"string"},"location":{"type":"string"},"website":{"type":"string"},"account":{"type":"string"}}},"AnalyzeProfileDto":{"type":"object","properties":{"handle":{"type":"string","example":"elonmusk"},"accountId":{"type":"string"}},"required":["handle"]},"ContentSuggestDto":{"type":"object","properties":{"format":{"type":"string","enum":["micro","punch","spark","hook","storm","thunder"]},"topic":{"type":"string"},"sourceHandles":{"type":"array","items":{"type":"string"}},"styleProfile":{"type":"object"}},"required":["format"]},"ViralScoreDto":{"type":"object","properties":{"text":{"type":"string"},"format":{"type":"string"},"handle":{"type":"string"}},"required":["text"]},"PublishTweetDto":{"type":"object","properties":{"accountId":{"type":"string"},"text":{"type":"string"},"scheduledAt":{"type":"string"}},"required":["accountId","text"]},"CreateAgentConfigDto":{"type":"object","properties":{"accountId":{"type":"string","example":"account-id-here"},"dailyTweetTarget":{"type":"number","example":3,"minimum":1,"maximum":20},"formatPreference":{"example":["punch","spark","hook"],"type":"array","items":{"type":"string"}},"topics":{"example":["tech","ai","startup"],"type":"array","items":{"type":"string"}},"toneOverride":{"type":"string","example":"casual and witty"},"scheduleIntervalMinutes":{"type":"number","example":120,"minimum":15,"maximum":1440}},"required":["accountId"]},"UpdateAgentConfigDto":{"type":"object","properties":{"enabled":{"type":"boolean"},"dailyTweetTarget":{"type":"number","minimum":1,"maximum":20},"formatPreference":{"type":"array","items":{"type":"string"}},"topics":{"type":"array","items":{"type":"string"}},"toneOverride":{"type":"object","nullable":true},"scheduleIntervalMinutes":{"type":"number","minimum":15,"maximum":1440}}},"UpdateStyleProfileDto":{"type":"object","properties":{"customInstructions":{"type":"string","example":"Write about AI and tech startups"},"tweetLanguage":{"type":"string","example":"tr"}}},"AnalyzeStyleDto":{"type":"object","properties":{"handle":{"type":"string","example":"elonmusk"}},"required":["handle"]},"ApproveDraftDto":{"type":"object","properties":{"scheduledAt":{"type":"string","example":"2026-05-28T10:00:00Z"}}},"EditDraftDto":{"type":"object","properties":{"text":{"type":"string","example":"Updated tweet text here"}},"required":["text"]},"EditAndApproveDraftDto":{"type":"object","properties":{"text":{"type":"string","example":"Updated tweet text here"},"scheduledAt":{"type":"string","example":"2026-05-28T10:00:00Z"}},"required":["text"]}}}}