{"openapi":"3.1.0","info":{"title":"dtk","description":"Self-hosted data API for Douyin and TikTok. Submit a link, get normalized content back.\n\nEvery response has the same envelope, errors included: `success`, `data`, `error`, `meta`. Error codes are stable and never translated; the message beside them is rendered in the language you asked for. Append `?lang=zh` to any endpoint, including this document, for Chinese.\n\n### How a request works\n\nFetching from a platform costs a real upstream call on a real identity, and it can take seconds. So the data endpoints are **asynchronous by default**: they queue the work and answer `202` immediately with a task id.\n\n```\nPOST /api/v1/parse            -> 202 {\"task_id\": \"...\", \"state\": \"queued\"}\nGET  /api/v1/tasks/{task_id}  -> 200 {\"state\": \"done\", \"data\": {...}}\n```\n\nThere are three ways to get the result, and they differ only in who does the waiting.\n\n**Poll the task.** `GET /api/v1/tasks/{task_id}` until `state` is `done` or `failed`. Always works, and is what a client with its own event loop should do.\n\n**Let the server wait — `?wait=`.** Add `?wait=10` and the connection is held until the task settles, up to that many seconds. This is how you make the call synchronous, and it is meant for clients that cannot poll at all: an iOS Shortcut, a shell one-liner, a spreadsheet.\n\n- Finished in time: `200`, with the result in `data` exactly as the task endpoint would have returned it. A task that failed comes back as a normal failure envelope, with its own status code.\n- Not finished in time: `202` with the task id and `state: \"running\"`. **This is not an error and nothing was lost** - the work is still running, and the same task id fetches it a moment later.\n- Above the instance's ceiling (`api.max_wait_seconds`, shown as `maximum` on the parameter): `400`. Rejected rather than quietly shortened, because a caller that asked to block for five minutes needs to learn it cannot, or it will read the early `202` as a failure.\n- Omitted, `0`, or negative: `0` and negative behave differently - `0` and omitting it both return `202` at once, and a negative value is a `400`.\n\nNothing changes internally: the work goes through the same queue either way. `?wait=` only decides who holds the connection.\n\n**Get called back — `callback_url`.** Supply one on submit and the finished result is POSTed there, so nothing polls and nothing blocks. The host must be on the operator's `security.url_allowlist`.\n\n### Reading a result twice\n\nA task result is kept for `retention.task_result_hours` and can be fetched as often as you like within it. Two identical requests made close together are joined onto one task rather than run twice; `?refresh=true` opts out of that and of the response cache, and spends an identity to do it.","version":"5.0.3","x-language":"en"},"paths":{"/api/v1/system/status":{"get":{"tags":["system"],"summary":"Instance status","description":"Version, component health, pool census and storage use.\n\nAuthenticated: it names component versions and row counts, which is more\nthan an unauthenticated probe has any reason to learn.","operationId":"system_status_api_v1_system_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"health","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/setup/status":{"get":{"tags":["setup"],"summary":"Whether this instance has an administrator yet","description":"Unauthenticated by necessity: it is what the wizard asks first.","operationId":"setup_status_api_setup_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"setup_status","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}]}},"/api/setup/init":{"post":{"tags":["setup"],"summary":"Create the first administrator account","description":"Consume the setup token and create the administrator.\n\nOrdering matters. The account check comes first so an initialized instance\nanswers 409 without ever looking at the token; the token is deleted the\nmoment it verifies, so two racing requests cannot both succeed.","operationId":"setup_init_api_setup_init_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupInit"}}},"required":true},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"setup_init","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}]}},"/api/v1/auth/login":{"post":{"tags":["auth"],"summary":"Exchange a password for a session cookie","description":"Sign in to the console and receive a session cookie.\n\nFor programs, prefer an API key over this endpoint: a key carries scopes\nand can be revoked on its own. Repeated failures are throttled, and too\nmany will lock the account out for a while.\n\n**Parameters**\n\n- `username` - the console account name.\n- `password` - that account's password.\n\n**Returns**\n\nThe signed-in principal, plus a `Set-Cookie` holding the session.","operationId":"login_api_v1_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_login","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}]}},"/api/v1/auth/demo":{"get":{"tags":["auth"],"summary":"The demo credentials, for the login page","description":"What to prefill the login form with, when this instance runs a demo.\n\nUnauthenticated, because the login page is: a visitor who has to sign in to\nlearn how to sign in has not been given a demo. That is safe only because of\nwhat it can return, and the boundaries are worth stating plainly.\n\nIt returns exactly one account's password - the one whose whole purpose is\nto be published - and only while ``demo.enabled`` is on. With the demo off\nit answers ``enabled: false`` and nothing else, so an instance that has\nnever run a demo, or has stopped, leaks nothing and looks the same as one\nthat was never built with the feature.\n\nThere is no other account this endpoint can name. It resolves the demo user\nby role, and the role cannot be assigned by hand\n(:func:`dtk.api.routes.admin.users._refuse_hand_made_demo`), so there is no\nway to make it print somebody else's password by creating a user.\n\n**Returns**\n\n``enabled``, and when it is true the username and password to prefill.","operationId":"demo_credentials_api_v1_auth_demo_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_demo","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/auth/logout":{"post":{"tags":["auth"],"summary":"Revoke the current session","description":"Idempotent on purpose: logging out twice is not an error.","operationId":"logout_api_v1_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_logout","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}]}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"The authenticated principal","description":"Who the current credential belongs to.\n\nWorks with either a session cookie or an API key, so it doubles as a way to\ncheck that a key is live and to see what it is allowed to do.\n\nOpen to the demo role, unlike everything else behind :data:`authenticated`.\nThis endpoint is how the console learns which role it is running as, and the\nconsole trims itself with that answer - the sidebar a demo visitor gets, and\nthe controls a page offers. Refusing it did not hide anything: the demo\naccount and its scopes are published in plaintext by ``/auth/demo`` while the\nswitch is on. What it did was leave the console unable to tell it was a demo,\nso it rendered the full navigation and every link in it answered 403.\n\n**Returns**\n\nThe account name, role and the scopes this credential carries.","operationId":"me_api_v1_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_me","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/auth/password":{"post":{"tags":["auth"],"summary":"Change your own password","description":"Requires the current password even inside an authenticated session.\n\nA stolen session should not be enough to take the account over, and an API\nkey must not be able to change the owner's password at all.","operationId":"change_password_api_v1_auth_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChange"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_password","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/auth/sessions":{"get":{"tags":["auth"],"summary":"List your live sessions","description":"Every device currently signed in as you.\n\n**Returns**\n\nOne entry per live session with where and when it signed in, and a flag\nmarking the one making this request.","operationId":"list_sessions_api_v1_auth_sessions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_sessions","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["auth"],"summary":"Log out every other device","description":"Sign out everywhere except here.\n\nThe session making the request is kept, so you are not logged out by your\nown call. Use this after changing a password, or if a device was lost.\n\n**Returns**\n\nHow many sessions were revoked.","operationId":"revoke_other_sessions_api_v1_auth_sessions_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_sessions_revoke_all","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/auth/sessions/{session_ref}":{"delete":{"tags":["auth"],"summary":"Revoke one of your sessions","description":"``session_ref`` is the digest from the list, never the token itself.","operationId":"revoke_one_session_api_v1_auth_sessions__session_ref__delete","parameters":[{"name":"session_ref","in":"path","required":true,"schema":{"type":"string","description":"The session digest from the list endpoint.","title":"Session Ref"},"description":"The session digest from the list endpoint."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"auth_session_revoke","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/ios/shortcut":{"get":{"tags":["ios"],"summary":"iOS Shortcut release information","description":"Version, link and notes in the requested language.\n\nUnauthenticated: the Shortcut asks for this before it has anywhere to put\nan API key, and the answer is public release metadata.","operationId":"shortcut_api_v1_ios_shortcut_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"ios_shortcut","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}]}},"/api/v1/tasks/{task_id}":{"get":{"tags":["tasks"],"summary":"Fetch a task","description":"The state of one task, and its result once it has finished.\n\nSafe to poll, and safe to call twice: the answer does not change until the\ntask does.\n\n**Parameters**\n\n- `task_id` - the id returned when the work was submitted.\n\n**Returns**\n\nThe task's state, timestamps and endpoint. A finished task also carries its\nresult. Results are kept for a limited time; once one has expired the\nlookup answers `TASK_NOT_FOUND`, which means submit the work again rather\nthan keep polling.","operationId":"get_task_api_v1_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The task id returned when the work was submitted.","title":"Task Id"},"description":"The task id returned when the work was submitted."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"task_get","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["tasks"],"summary":"Cancel a task","description":"Give up on a task that has not started.\n\nOnly a queued task is stopped. One that is already running is left alone\nand reported as running: the request is in flight against a platform and\nthe identity's quota is already spent, so recording a failure the worker\nnever had would make the endpoint's risk rate lie - and the circuit breaker\nreads that rate.\n\nNothing is deleted. The row stays, with its state, the way every other\nfinished task does.\n\n**Returns**\n\nThe task's state after the request: `failed` if it was cancelled, or\nwhatever it already was.","operationId":"cancel_task_api_v1_tasks__task_id__delete","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The task id returned when the work was submitted.","title":"Task Id"},"description":"The task id returned when the work was submitted."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"task_cancel","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tasks/{task_id}/events":{"get":{"tags":["tasks"],"summary":"Stream a task's progress","description":"Server-sent events until the task settles or the deadline passes.\n\nAuthorization happens before the response starts; once the stream is open\nthere is nothing left to authorize.","operationId":"task_events_api_v1_tasks__task_id__events_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The task id returned when the work was submitted.","title":"Task Id"},"description":"The task id returned when the work was submitted."},{"name":"timeout","in":"query","required":false,"schema":{"type":"number","maximum":300,"minimum":1,"description":"Seconds to hold the stream open before closing it.","default":300,"title":"Timeout"},"description":"Seconds to hold the stream open before closing it."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"task_events","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/parse":{"post":{"tags":["content"],"summary":"Parse any supported link","description":"The front door: hand it a link or the share text around one.\n\nAccepts either platform, so it requires read access to one of them; the\nworker decides which after expanding the URL.","operationId":"parse_api_v1_parse_post","parameters":[{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"parse","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tasks/batch":{"post":{"tags":["content"],"summary":"Submit many links at once","description":"One round trip, N independent tasks.\n\nSubmission is batched, tracking is not: each item gets its own task id and\nits own fate, so one bad link cannot smear the whole request into a single\nerror the way a synchronous bulk endpoint would (doc 07).","operationId":"batch_api_v1_tasks_batch_post","parameters":[{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchRequest"}}}},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"batch","x-statuses":["202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/video":{"get":{"tags":["content"],"summary":"One post, video or image album","description":"One post: a video, or an image album, with its author and statistics.\n\nIdentify the post by **either** `url` **or** `aweme_id` - exactly one is\nrequired. A share link works, including the shortened `v.douyin.com` and\n`vm.tiktok.com` forms, and a link that carries the id needs no id.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `url` - a link to the post. Text with a link inside it is accepted, so\n  the clipboard content the apps produce can be sent unedited.\n- `aweme_id` - the post id, if you already have it.\n- `include_raw` - also return the platform's own untouched payload.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.content_ttl` (30 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nThe post's media URLs, cover, caption, statistics, author and timestamps,\nnormalized to the same shape for both platforms.","operationId":"video_api_v1__platform__video_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"aweme_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"The post id, as an alternative to url. Provide exactly one of the two.","title":"Aweme Id"},"description":"The post id, as an alternative to url. Provide exactly one of the two."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"content_detail","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/video/comments":{"get":{"tags":["content"],"summary":"Top level comments on a post","description":"One page of top level comments on a post.\n\nIdentify the post by **either** `url` **or** `aweme_id`. To read the\nreplies under a comment, use `/video/comments/replies`.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `url` - a link to the post.\n- `aweme_id` - the post id, if you already have it.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - comments per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nComment text, author, like count, reply count and timestamp, plus the\ncursor for the next page.","operationId":"comments_api_v1__platform__video_comments_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"aweme_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"The post id, as an alternative to url. Provide exactly one of the two.","title":"Aweme Id"},"description":"The post id, as an alternative to url. Provide exactly one of the two."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"comments","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/video/comments/replies":{"get":{"tags":["content"],"summary":"Replies under one comment","description":"One page of replies underneath a single comment.\n\nBoth the comment and the post it belongs to are required: pass\n`comment_id` together with **either** `url` **or** `aweme_id`. Comment ids\ncome from `/video/comments`.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `comment_id` - the parent comment to read replies under.\n- `url` - a link to the post the comment is on.\n- `aweme_id` - that post's id, if you already have it.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - replies per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nThe same comment shape as `/video/comments`, plus the cursor for the next\npage.","operationId":"comment_replies_api_v1__platform__video_comments_replies_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"comment_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The parent comment whose replies you want; ids come from the comments endpoint.","title":"Comment Id"},"description":"The parent comment whose replies you want; ids come from the comments endpoint."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"aweme_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"description":"The post id, as an alternative to url. Provide exactly one of the two.","title":"Aweme Id"},"description":"The post id, as an alternative to url. Provide exactly one of the two."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"comment_replies","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/user":{"get":{"tags":["content"],"summary":"Author profile","description":"One author's public profile.\n\nIdentify the author by **either** `url` **or** `sec_user_id` - exactly one\nis required. A profile link is enough; the id is read out of it.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `url` - a link to the author's profile page.\n- `sec_user_id` - the author's stable id, if you already have it. On\n  TikTok this is `secUid`.\n- `include_raw` - also return the platform's own untouched payload.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.author_ttl` (15 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nNickname, signature, avatar, verification state, and follower, following,\nlike and post counts.","operationId":"user_api_v1__platform__user_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"sec_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"The author's stable id, as an alternative to url. On TikTok this is secUid.","title":"Sec User Id"},"description":"The author's stable id, as an alternative to url. On TikTok this is secUid."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"author_profile","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/user/posts":{"get":{"tags":["content"],"summary":"Author post list","description":"One page of an author's own posts, newest first.\n\nIdentify the author by **either** `url` **or** `sec_user_id`. Page through\nthe feed with `cursor`.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `url` - a link to the author's profile page.\n- `sec_user_id` - the author's stable id, if you already have it. On\n  TikTok this is `secUid`.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - posts per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nThe same post shape as `/video`, one entry per post, plus the cursor for\nthe next page.","operationId":"user_posts_api_v1__platform__user_posts_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"sec_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"The author's stable id, as an alternative to url. On TikTok this is secUid.","title":"Sec User Id"},"description":"The author's stable id, as an alternative to url. On TikTok this is secUid."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"author_posts","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/user/likes":{"get":{"tags":["content"],"summary":"Posts an author has liked","description":"One page of the posts an author has publicly liked.\n\nAvailability differs by platform, measured 2026-09-08:\n\n- **TikTok** answers a guest request. Accounts hide their likes by default,\n  so an empty page usually means the author keeps the list private rather\n  than that anything failed.\n- **Douyin** does not serve this list to a guest identity at all - the same\n  identity that reads posts and mixes gets an empty response here. It needs\n  an imported logged-in identity; see the console's identity import.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`. Must match the link you pass.\n- `url` - a link to the author's profile page.\n- `sec_user_id` - the author's stable id, if you already have it.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - posts per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nThe same post shape as `/video`, plus the cursor for the next page.","operationId":"user_likes_api_v1__platform__user_likes_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"sec_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"The author's stable id, as an alternative to url. On TikTok this is secUid.","title":"Sec User Id"},"description":"The author's stable id, as an alternative to url. On TikTok this is secUid."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"author_likes","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/mix/posts":{"get":{"tags":["content"],"summary":"Posts inside a mix or playlist","description":"One page of the posts collected in a mix.\n\nA mix is Douyin's series and TikTok's playlist - an ordered set an author\ngroups their own posts into. The id comes from any post that belongs to\none: Douyin returns it as `mix_info`, TikTok as `playlistId`.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`.\n- `mix_id` - the mix or playlist to read.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - posts per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nThe same post shape as `/video`, plus the cursor for the next page.","operationId":"mix_posts_api_v1__platform__mix_posts_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"mix_id","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"description":"The mix or playlist to read; Douyin calls it mix_info, TikTok playlistId.","title":"Mix Id"},"description":"The mix or playlist to read; Douyin calls it mix_info, TikTok playlistId."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"mix_posts","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/user/followers":{"get":{"tags":["content"],"summary":"Accounts that follow an author","description":"One page of the accounts that follow an author.\n\n**TikTok only.** Douyin answers this with \"not signed in\" for any guest\nidentity, so the endpoint is not offered there and asking returns a\n`UNSUPPORTED_CONTENT` error naming the platforms that do serve it.\n\n**Parameters**\n\n- `platform` - must be `tiktok`.\n- `url` - a link to the author's profile page.\n- `sec_user_id` - the author's stable id, if you already have it.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - accounts per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nOne author record per follower - nickname, avatar, signature and counts -\nplus the cursor for the next page.","operationId":"user_followers_api_v1__platform__user_followers_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"sec_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"The author's stable id, as an alternative to url. On TikTok this is secUid.","title":"Sec User Id"},"description":"The author's stable id, as an alternative to url. On TikTok this is secUid."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"author_followers","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/{platform}/user/following":{"get":{"tags":["content"],"summary":"Accounts an author follows","description":"One page of the accounts an author follows.\n\n**TikTok only**, for the same reason as `/user/followers`. Accounts hide\nthis side of the graph far more often than they hide their followers, so an\nempty page is a common and correct answer even on TikTok.\n\n**Parameters**\n\n- `platform` - must be `tiktok`.\n- `url` - a link to the author's profile page.\n- `sec_user_id` - the author's stable id, if you already have it.\n- `cursor` - the cursor returned by the previous page. Omit it for the\n  first page; a response with no cursor is the last page.\n- `count` - accounts per page.\n- `include_raw` - include each item's untouched platform payload. A page\n  carries one per item, so this multiplies the response and everything\n  that stores it; it is off by default for that reason.\n- `wait` - seconds to wait for the result. Omit it to get `202` and a task\n  id to poll.\n- `identity` - send the request as this identity and no other. For content\n  only one account can see. Requires `identity:manage`.\n- `refresh` - ignore any cached or in-flight answer and ask upstream\n  again. Without it a repeat inside `cache.list_ttl` (5 minutes by default)\n  is answered from the cache and costs nothing; a refresh costs an\n  identity and a real request, and its answer is cached in turn.\n\n**Returns**\n\nOne author record per followed account, plus the cursor for the next page.","operationId":"user_following_api_v1__platform__user_following_get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"$ref":"#/components/schemas/Platform","description":"Platform the request is addressed to: douyin or tiktok."},"description":"Platform the request is addressed to: douyin or tiktok."},{"name":"url","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited.","title":"Url"},"description":"A link to the resource. Text with a link inside it is accepted, so clipboard content from the apps can be sent unedited."},{"name":"sec_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"The author's stable id, as an alternative to url. On TikTok this is secUid.","title":"Sec User Id"},"description":"The author's stable id, as an alternative to url. On TikTok this is secUid."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Opaque cursor from the previous page; omit for the first page.","title":"Cursor"},"description":"Opaque cursor from the previous page; omit for the first page."},{"name":"count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Items per page.","title":"Count"},"description":"Items per page."},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","description":"Include the untouched platform payload.","default":false,"title":"Include Raw"},"description":"Include the untouched platform payload."},{"name":"wait","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document.","title":"Wait","maximum":30.0},"description":"Hold the connection until the task finishes, up to this many seconds - this is how to make the call synchronous. Finished in time gives 200 with the result; not finished gives 202 with the task id and `state: running`, which is not an error and loses nothing. Above the instance ceiling shown as `maximum` it is a 400, rejected rather than shortened. Omitted or 0 returns 202 at once. See the description at the top of this document."},{"name":"proxy","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy.","title":"Proxy"},"description":"Send the upstream request through this proxy, as a full URL. Refused unless an administrator has enabled security.request_proxy."},{"name":"identity","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":36},{"type":"null"}],"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache.","title":"Identity"},"description":"Send the request as this identity and no other, by id. For content only that account can see, such as your own private posts, fetched with a jar you imported from your own browser. Requires identity:manage; the request is never served from another identity and never from the response cache."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound.","default":false,"title":"Refresh"},"description":"Ignore any cached or in-flight answer and make the request again. Two things make a repeat return the same thing - this instance joins an identical task that is already running or recently finished, and it caches the shaped body - and this turns off both. The fresh answer is still cached. It costs an identity and an upstream request, so it is for checking whether something changed, not for every call. How long the cached answer lives depends on what was asked for and is set in the console: cache.content_ttl for one post (30 minutes by default), cache.author_ttl for a profile (15 minutes), cache.list_ttl for anything paged (5 minutes). Entries expire on their own and Redis is capped below its container limit, dropping the least recently used expiring keys before it fills, so the cache cannot grow without bound."},{"name":"explain","in":"query","required":false,"schema":{"type":"boolean","description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make.","default":false,"title":"Explain"},"description":"Return the request as it went out - the signed URL, the headers and the identity's cookie jar - so it can be replayed outside this instance. The answer contains a credential, so it needs `identity:manage` and an operator role, it is written to the audit log, and it is stripped from the stored task result for any reader without that scope. Implies `refresh`: an explanation of a cached answer would describe a call this request did not make."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"author_following","x-statuses":["200","202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive":{"get":{"tags":["archive"],"summary":"Search the archive","description":"One page of what this instance has collected, newest sighting first.\n\nAnswers from local storage: no identity is spent, nothing is fetched, and a\npost that has since been deleted is still here with `availability` saying so.\n\n**Parameters**\n\n- `platform`, `author_uid`, `tag`, `kind`, `duration_bucket`, `availability` -\n  narrow the result; all optional and combinable.\n- `collection` - only posts in this collection. The one filter here that is\n  not a property of the post: somebody put them in it.\n- `stored` - true for the posts whose media is on this instance's disk right\n  now. An evicted download does not count: the record is kept on purpose,\n  and the bytes are not there to play.\n- `q` - substring of the title or description. Matched as a substring rather\n  than by word, so it behaves the same in Chinese as in English.\n- `cursor` - the cursor from the previous page. Omit it for the first page;\n  a response with no cursor was the last page.\n- `limit` - rows per page.\n\n**Returns**\n\nThe posts, each with its author, tags, derived classification and media\nmanifest, plus the cursor for the next page.","operationId":"list_archive_api_v1_archive_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only this platform.","title":"Platform"},"description":"Only this platform."},{"name":"author_uid","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"Only this author's posts.","title":"Author Uid"},"description":"Only this author's posts."},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only posts carrying this tag.","title":"Tag"},"description":"Only posts carrying this tag."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ContentKind"},{"type":"null"}],"description":"Only posts of this kind.","title":"Kind"},"description":"Only posts of this kind."},{"name":"duration_bucket","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/DurationBucket"},{"type":"null"}],"description":"Only posts in this length class.","title":"Duration Bucket"},"description":"Only posts in this length class."},{"name":"availability","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Availability"},{"type":"null"}],"description":"Only posts with this upstream status.","title":"Availability"},"description":"Only posts with this upstream status."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Substring of the title or description. Matches in any script.","title":"Q"},"description":"Substring of the title or description. Matches in any script."},{"name":"collection","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Only posts in this collection, by id. Unlike every other filter here this one is not a property of the post: somebody put them in it.","title":"Collection"},"description":"Only posts in this collection, by id. Unlike every other filter here this one is not a property of the post: somebody put them in it."},{"name":"stored","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"True for posts whose media this instance is holding right now, false for the rest. Like `collection` this describes what was kept rather than the post: an evicted download does not count, because there is nothing left to play.","title":"Stored"},"description":"True for posts whose media this instance is holding right now, false for the rest. Like `collection` this describes what was kept rather than the post: an evicted download does not count, because there is nothing left to play."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"description":"Cursor from the previous page; omit for the first.","title":"Cursor"},"description":"Cursor from the previous page; omit for the first."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"description":"Rows per page.","title":"Limit"},"description":"Rows per page."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/stats":{"get":{"tags":["archive"],"summary":"Archive totals","description":"How much this instance has stored.\n\n**Returns**\n\nTotal posts and authors, a per-platform breakdown, and how many of them\nthis instance is holding the media for - in total and per platform. The\narchive is what was seen and a download is what was kept, so the two counts\nare different questions and reporting only the first made \"46 posts\" read\nas 46 videos on the disk.","operationId":"archive_stats_api_v1_archive_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_stats","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/export":{"get":{"tags":["archive"],"summary":"Export the archive","description":"Stream every matching post as newline-delimited JSON.\n\nStreamed a page at a time rather than assembled in memory: an archive is\nmeant to grow past what fits in one response, and buffering it would make\nthe size of your own data the thing that breaks the export.\n\nNeeds `archive:export`, which ordinary read keys do not carry - this is the\none call that hands back the whole collection at once.\n\n**Parameters**\n\nThe same filters as the list endpoint. Export what you can already see.\n\n**Returns**\n\n`application/x-ndjson`: one post per line, in the same shape the list\nendpoint returns, without the media manifest - use the list endpoint or\n`/archive/{platform}/{content_id}` when you need the mirrors.","operationId":"export_archive_api_v1_archive_export_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only this platform.","title":"Platform"},"description":"Only this platform."},{"name":"author_uid","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":256},{"type":"null"}],"description":"Only this author's posts.","title":"Author Uid"},"description":"Only this author's posts."},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only posts carrying this tag.","title":"Tag"},"description":"Only posts carrying this tag."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/ContentKind"},{"type":"null"}],"description":"Only posts of this kind.","title":"Kind"},"description":"Only posts of this kind."},{"name":"duration_bucket","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/DurationBucket"},{"type":"null"}],"description":"Only posts in this length class.","title":"Duration Bucket"},"description":"Only posts in this length class."},{"name":"availability","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Availability"},{"type":"null"}],"description":"Only posts with this upstream status.","title":"Availability"},"description":"Only posts with this upstream status."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Substring of the title or description. Matches in any script.","title":"Q"},"description":"Substring of the title or description. Matches in any script."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"One post per line.","content":{"application/x-ndjson":{}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_export","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/recheck":{"post":{"tags":["archive"],"summary":"Re-check what still exists","description":"Verify that archived posts still exist on the platform.\n\nThis is what makes \"which of the things I saved are gone\" answerable. A\ndeleted post answers the platform's own not-found, which is the finding\nrather than a failure: the record is kept and marked `deleted`, so it stays\nsearchable and exportable with the truth attached.\n\nEach post is one real request through the identity pool, so the batch is\nbounded and the pass runs in the background.\n\n**Parameters**\n\n- `limit` - how many posts to verify, least recently checked first.\n- `older_than_days` - only posts whose last check is older than this.\n\n**Returns**\n\n`202` with a task id. The result carries how many were checked and how many\nturned out to be gone.","operationId":"recheck_api_v1_archive_recheck_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecheckRequest"}}},"required":true},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_recheck","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/backfill":{"post":{"tags":["archive"],"summary":"Archive an author's history","description":"Walk one author's posts past the first page and archive every one.\n\nSeparate from the watchlist on purpose. A watchlist entry is for what is\nnew and runs forever; a backfill is a one-off with a very different cost,\nand making every scheduled run as deep as the deepest anyone ever wanted\nwould be the wrong trade.\n\nStops at the first page that returns nothing, at the page ceiling, or when\nthe platform says there is no more history - whichever comes first.\n\n**Parameters**\n\n- `platform`, `author_id` - whose history to walk.\n- `pages` - how deep to go, capped by the server.\n\n**Returns**\n\n`202` with a task id. The result says how many pages were walked, how many\nposts were archived, and why it stopped.","operationId":"backfill_api_v1_archive_backfill_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackfillRequest"}}},"required":true},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_backfill","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/collections":{"get":{"tags":["archive"],"summary":"List collections","description":"Every collection, with how many posts is in each.\n\nA collection is the one grouping the library offers that is not derived\nfrom the posts: author, platform and collection date all come out of the\nrecord, and this one comes out of somebody deciding.\n\n**Returns**\n\nEach collection's id, name, note, item count and timestamps, newest first.","operationId":"list_collections_api_v1_archive_collections_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_list","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["archive"],"summary":"Create a collection","description":"Make a new named set. Empty until posts are added to it.\n\n**Parameters**\n\n- `name` - unique, case-insensitively. Whitespace is collapsed.\n- `note` - free text the console shows under the name. Never parsed.\n\n**Returns**\n\nThe collection.","operationId":"create_collection_api_v1_archive_collections_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_create","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/collections/{collection_id}":{"patch":{"tags":["archive"],"summary":"Rename a collection","description":"Change the name, the note, or both. Membership is untouched.\n\n**Parameters**\n\n- `name` - the new name, if it is changing.\n- `note` - the new note. Send it as null to clear it; leave it out\n  entirely to keep whatever is there.\n\n**Returns**\n\nThe collection.","operationId":"update_collection_api_v1_archive_collections__collection_id__patch","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The collection to change.","title":"Collection Id"},"description":"The collection to change."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CollectionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_update","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["archive"],"summary":"Delete a collection","description":"Remove the collection itself.\n\nThe posts stay in the archive and the files stay on disk: a collection is a\nlabel, and deleting a label is not deleting what it was on.\n\n**Returns**\n\n`{\"deleted\": true}`.","operationId":"delete_collection_api_v1_archive_collections__collection_id__delete","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The collection to delete.","title":"Collection Id"},"description":"The collection to delete."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_delete","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/collections/{collection_id}/items":{"post":{"tags":["archive"],"summary":"Add posts to a collection","description":"Put posts in a collection.\n\nAdding a post that is already in it is a no-op rather than an error: the\nconsole sends whatever is selected, and part of a selection is routinely\nalready there. A post that is not in the archive is skipped, so one stale\ncard does not refuse the other nineteen.\n\n**Parameters**\n\n- `items` - the posts, each as a platform and a content id.\n\n**Returns**\n\n`added`, the number that were not already in it.","operationId":"add_to_collection_api_v1_archive_collections__collection_id__items_post","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The collection to add to.","title":"Collection Id"},"description":"The collection to add to."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSelection"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_add","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/collections/{collection_id}/items/remove":{"post":{"tags":["archive"],"summary":"Take posts out of a collection","description":"Take posts out of a collection, leaving them in the archive.\n\nA POST rather than a DELETE because it carries a body, and a DELETE with a\nbody is the kind of thing intermediaries drop.\n\n**Returns**\n\n`removed`, the number of memberships that went.","operationId":"remove_from_collection_api_v1_archive_collections__collection_id__items_remove_post","parameters":[{"name":"collection_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The collection to remove from.","title":"Collection Id"},"description":"The collection to remove from."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentSelection"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"collections_remove","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/delete":{"post":{"tags":["archive"],"summary":"Delete archived posts","description":"Remove posts from the archive, and by default their stored media too.\n\nThis is the one call in the archive that destroys something. It is not\nreversible and there is no trash: the row goes, its collection memberships\ngo with it, and unless `media` is false the sidecar is asked to remove the\ndirectories on disk as well.\n\nThe files are removed first and the rows second. The other order can leave a\nrecord pointing at a directory that is already gone, which reads as a\ndownload the console can offer and cannot deliver; this order can at worst\nleave bytes with no record, which the storage panel already reports.\n\n**Parameters**\n\n- `items` - the posts, each as a platform and a content id.\n- `media` - also delete what is on disk. True by default, because that is\n  what \"delete this\" means about a video the instance is holding.\n\n**Returns**\n\nHow many archive rows went, how many download records went, and how many\nbytes the sidecar reported freeing.","operationId":"delete_archived_api_v1_archive_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArchiveDelete"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_delete","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/archive/{platform}/{content_id}":{"get":{"tags":["archive"],"summary":"One archived post","description":"One post as this instance last saw it.\n\n**Returns**\n\nThe stored record, media manifest included. The signed CDN links inside it\nexpire; `web_url` is the part that survives.","operationId":"get_archived_api_v1_archive__platform___content_id__get","parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string","description":"douyin or tiktok.","title":"Platform"},"description":"douyin or tiktok."},{"name":"content_id","in":"path","required":true,"schema":{"type":"string","maxLength":64,"description":"The post id.","title":"Content Id"},"description":"The post id."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"archive_get","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads":{"post":{"tags":["downloads"],"summary":"Store a post's media","description":"Fetch one post's media onto this instance's disk.\n\nTakes a post id or a link. A link is not a URL this endpoint will fetch:\nit goes through `dtk.urls.identify`, which is the allowlist doc 08 puts in\nfront of every caller-supplied URL, and only the post id it yields is used.\nThe request then goes to the platform's own endpoint table exactly as if the\nid had been typed.\n\nThe post does not have to be archived. If this instance has never seen it,\nthe worker fetches it first - through the same pool, scheduler and request\nlog as any other read - and downloads what comes back. A downloader that\ncould only save what you happened to parse first is two steps where people\nexpect one.\n\nShort links are the exception: resolving `v.douyin.com/xxxx` means following\nit, and this endpoint does not make network calls. Send those to `/parse`,\nwhich expands them in the background, and the post is archived by the time\nyou come back.\n\n**Parameters**\n\n- `url` - a share link, or the share text around one.\n- `platform`, `content_id` - the post key, if you already have it. A link\n  supplies both; giving both a link and a key that disagree is refused\n  rather than guessed at.\n- `skip_existing` - hand back the download this instance already has rather\n  than fetching the post again. What re-running a feed wants: the author\n  added three posts and the other forty are on the disk already.\n\nA download already in flight for the same post is joined whatever\n`skip_existing` says, and that is not a preference. Two downloads of one\npost write into one directory, and the sidecar renames `name.part` to\n`name` as it finishes - so the loser of that race renames a file the winner\nhas already moved, and comes back `partial` with a file missing.\n\n**Returns**\n\n`202` with a `download_id` and the `task_id` running it. Poll the download\nor the task; the files land on the operator's media volume and are never\nserved back through this API.","operationId":"start_download_api_v1_downloads_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadRequest"}}}},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_create","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"get":{"tags":["downloads"],"summary":"List downloads","description":"What this instance has been asked to store, newest first.\n\nA row survives its files: one whose `on_disk` is false was collected and\nlater removed to stay under the media ceiling, which is a different fact\nfrom never having been fetched - and unlike that one, it can be undone by\nasking again.\n\n**Returns**\n\nThe downloads with their per-file digests and sizes, plus the total.","operationId":"list_downloads_api_v1_downloads_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only this platform.","title":"Platform"},"description":"Only this platform."},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/DownloadState"},{"type":"null"}],"description":"Only downloads in this state.","title":"State"},"description":"Only downloads in this state."},{"name":"pinned","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only pinned downloads, or only unpinned.","title":"Pinned"},"description":"Only pinned downloads, or only unpinned."},{"name":"on_disk","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"True for downloads whose files are still stored.","title":"On Disk"},"description":"True for downloads whose files are still stored."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":500,"minimum":1},{"type":"null"}],"description":"Rows per page.","title":"Limit"},"description":"Rows per page."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Rows to skip.","default":0,"title":"Offset"},"description":"Rows to skip."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/retry":{"post":{"tags":["downloads"],"summary":"Try a download again","description":"Start a settled download over.\n\nThere is no resume, and there could not usefully be one. The sidecar writes\nto `<name>.part` and truncates it on every attempt, and the reason it is\nbuilt that way is upstream: media URLs are signed and expire, so bytes\nfetched an hour ago cannot be continued against a link that now answers 403.\nWhat works instead is what this does - ask again, which re-parses the post\nfor fresh mirrors first if the stored ones have gone stale.\n\nRefused while the download is still going. A stuck one is failed by the\nmaintenance pass after `maintenance.stale_download_seconds`, and until then\nthere is nothing to retry: a second attempt would race the first into the\nsame directory.\n\n**Parameters**\n\n- `download_id` - the settled download to try again.\n\n**Returns**\n\n`202` with the new `download_id` and the task running it.","operationId":"retry_download_api_v1_downloads_retry_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryRequest"}}},"required":true},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_retry","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/deduplicate":{"post":{"tags":["downloads"],"summary":"Remove duplicate downloads","description":"Keep one copy of each post and remove the rest.\n\nRe-downloading a post is a normal thing to do - the first attempt failed,\nthe mirrors went stale, the file was evicted - and each attempt leaves a\nrow. The successful ones leave a complete second copy of the same video,\nwhich is the disk filling with bytes nobody asked for twice.\n\nThe newest copy that still has its files is kept: newest because it came\nfrom the freshest mirrors, still-present because keeping an evicted row over\na present one would delete the only copy there is.\n\nA directory is only removed when no surviving row shares it. Two downloads\nof one post land in the same directory - it is keyed by platform, author and\npost - so deleting the older row's path by id would delete the file the\nnewer one just wrote. In that ordinary case the rows go and the bytes stay,\nwhich is the correct outcome: there was only ever one copy on disk.\n\n**Parameters**\n\n- `dry_run` - report what would go and change nothing.\n\n**Returns**\n\nHow many duplicate records were found, how many were removed, the bytes the\nsidecar reported freeing, and the posts involved.","operationId":"deduplicate_api_v1_downloads_deduplicate_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DedupeRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_dedupe","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/storage":{"get":{"tags":["downloads"],"summary":"Media storage usage","description":"How much disk stored media occupies, and how much it is allowed to.\n\nThe measured total comes from the volume rather than from the database, so\nit accounts for files removed by hand and for anything a restore put back.\n\n**Returns**\n\nTotals per state, the measured volume usage, the configured ceiling, and\nwhether the downloader is reachable at all.","operationId":"storage_api_v1_downloads_storage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_storage","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/{download_id}":{"get":{"tags":["downloads"],"summary":"One download","description":"One download and every file in it.\n\n**Returns**\n\nThe stored record: per-file name, size, sha256 and content type, plus\nwhether the files are still on the disk.","operationId":"get_download_api_v1_downloads__download_id__get","parameters":[{"name":"download_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The download id returned when it was started.","title":"Download Id"},"description":"The download id returned when it was started."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_get","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["downloads"],"summary":"Cancel a download","description":"Stop a download that has not finished.\n\nCancels the transfer; it does not delete anything. Files already complete\nstay where they are, and the record stays either way - removing files is\nthe cleanup sweep's job and follows the operator's ceiling, not a DELETE.\n\n**Returns**\n\nThe download's state after the request.","operationId":"cancel_download_api_v1_downloads__download_id__delete","parameters":[{"name":"download_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The download id returned when it was started.","title":"Download Id"},"description":"The download id returned when it was started."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_cancel","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/{download_id}/files/{name}":{"get":{"tags":["downloads"],"summary":"Download one stored file","description":"Serve one file this instance has already stored, to a browser.\n\nThe path is rebuilt from the stored row - the download's own directory plus\na name that has to appear in its file list - so the only thing a caller\nchooses is *which* record, never where on the disk to read. A name that is\nnot in the record is a 404 whether or not something of that name exists.\n\nNothing is fetched: if the files were evicted to stay under the media\nceiling the record survives without them, and this answers 404 rather than\ngoing back to the platform. Ask for the download again to restore it.\n\n**Parameters**\n\n- `download_id` - the record, from `GET /downloads`.\n- `name` - one of the names in that record's `files`.\n\n**Returns**\n\nThe file, with the content type the downloader recorded for it and a\nContent-Disposition that makes a browser save rather than render it.","operationId":"get_file_api_v1_downloads__download_id__files__name__get","parameters":[{"name":"download_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The download id returned when it was started.","title":"Download Id"},"description":"The download id returned when it was started."},{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"The file's name, exactly as the download record lists it.","title":"Name"},"description":"The file's name, exactly as the download record lists it."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"The stored file.","content":{"application/octet-stream":{}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_file","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/downloads/{download_id}/pin":{"post":{"tags":["downloads"],"summary":"Pin or unpin","description":"Exempt one download from the size-based cleanup, or stop exempting it.\n\nThis is the only way to keep a file the ceiling would otherwise remove.\nWithout it, a policy that deletes oldest-first eventually takes the one\npost somebody meant to keep - and a post the platform has since removed\ncannot be fetched again.\n\n**Parameters**\n\n- `pinned` - true to keep it, false to let the sweep have it.","operationId":"pin_download_api_v1_downloads__download_id__pin_post","parameters":[{"name":"download_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The download id returned when it was started.","title":"Download Id"},"description":"The download id returned when it was started."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PinRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"downloads_pin","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tools/sign":{"post":{"tags":["tools"],"summary":"Sign a platform API URL","description":"Compute the signature parameters a platform API URL needs.\n\nPure arithmetic: nothing is fetched, no identity is spent, and the result\ndepends only on what you send. Give it the full URL including its query and\nit returns that query with the signature appended, ready to send.\n\nThree things have to line up between signing and sending, or the platform\nrefuses the request. Measured 2026-09-08 by sending this endpoint's own\noutput: with all three, 2537 bytes and the expected profile; with no cookies\nat all, 200 and an empty body.\n\n- **The User-Agent.** Both platforms hash it into the signature. The one\n  used is echoed back; send that exact string.\n- **The TLS fingerprint.** TikTok checks that it agrees with the User-Agent,\n  so a Chrome UA has to travel over a Chrome TLS profile. Plain `curl` is\n  refused whatever the signature says.\n- **A cookie jar.** A correct signature with no cookies still gets nothing:\n  the platform answers a *session*, and `msToken` and `UIFID_TEMP` are\n  issued to a browser rather than computed. `/tools/identity` is how to get\n  one.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`.\n- `url` - the API URL to sign, query included.\n- `user_agent` - what the request will be sent with; a Chrome default is\n  used if you omit it.\n- `ms_token` - TikTok only. Sealed into the signature, so it must be the\n  token the request will carry. An invented value is worse than none.\n- `cookies` - the jar the request will be sent with, in any paste format\n  (`Cookie:` header, DevTools JSON, Netscape). Douyin computes\n  `x-secsdk-web-signature` over the visitor id inside it, so without it\n  `headers` comes back empty and the sign-protected endpoints refuse the\n  request naming `uifid`. `/tools/identity` mints a jar that has it.\n\n**Returns**\n\nThe signed query string, the parameters that were added, any headers the\nsignature requires, and the User-Agent the signature was computed with.\n\n`stages` is that same result unflattened: one entry per layer of the\npipeline, in the order the layers ran, each naming the parameters and\nheaders it contributed and whether it ran at all.\n\n- `business` - what you put in the URL.\n- `session` - the `msToken`. `ms_token_source` says where it came from;\n  `generated` means Douyin invented one, which is the only value here you\n  did not supply. TikTok never invents: with no token it is skipped with\n  `no_ms_token_supplied` and an empty `msToken` is sent, because a\n  fabricated one is verified and fails while a missing one is accepted.\n- `signature` - the seal. `seal_param` names the parameter that is actually\n  it, which on TikTok is `X-Gnarly`: `algorithm` reports `X-Bogus` because\n  that is the stable name of the scheme, but the X-Bogus sent is the\n  constant `1`. `roles` says the same per parameter.\n  `input_reconstructible` is false - the bytes this layer signed are not\n  always the bytes finally sent, so this endpoint will not guess at them.\n- `websign` - Douyin only. When the jar carried no visitor id it is present\n  and skipped with `no_uifid_cookie`, which is the reason `headers` came\n  back empty and the reason a sign-protected endpoint will refuse you. When\n  it ran it also carries the `salt` and the `preimage` its md5 covers,\n  verified against the returned signature before being returned.\n\nSend `query` byte for byte - re-encoding it changes the bytes the signature\ncovers - and send every header in `headers` alongside it.","operationId":"sign_api_v1_tools_sign_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"tools_sign","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tools/decode":{"post":{"tags":["tools"],"summary":"Read a signature parameter back","description":"Take a signature parameter apart and say what is inside it.\n\nThe inverse of `/tools/sign`, and the reason this project reversed these\nalgorithms itself rather than vendoring somebody's port: an implementation\nyou own can be explained. Paste a whole signed URL and every parameter in it\nis decoded against the exact string that parameter seals.\n\nPure arithmetic. Nothing is fetched, no identity is spent, and the result\ndepends only on what you send.\n\n**What \"decode\" means here** - three different things, and every field says\nwhich one it is, because running them together is how a tool like this\nstarts lying.\n\n- **Recovered** (`plain`, `time`, `environment`). In the payload, and it\n  comes back exactly: clocks, `aid`, `page_id`, the screen geometry the page\n  reported, SDK and SCM versions, call counters, nonces.\n- **Bound but not recoverable** (`digest`). The payload carries a hash *of*\n  something - three SM3 bytes of the query in `a_bogus`, a whole md5 of it\n  in `X-Gnarly`. A hash does not run backwards and this endpoint will not\n  pretend it does. Instead it *checks*: send `user_agent`, or a URL that\n  carries the query, and `checks` says whether that candidate is the one the\n  signature was computed over, and how many bits say so.\n- **Not computed at all**. `msToken` and the visitor tokens are issued or\n  drawn, not derived. There is no plaintext under them; `reason` says\n  `not_computed` rather than reporting a failure.\n\n**Parameters**\n\n- `value` - a signed URL, a query string, a `name=value` pair, or a bare\n  parameter value. A URL is much the most useful form: only then is the\n  covered string known exactly, so only then can the checks run.\n- `parameter` - name it yourself when the shape is ambiguous. Otherwise it\n  is identified by properties no other parameter in the set has.\n- `user_agent` - a candidate to check against the signature. Both platforms\n  hash the UA in, so this is how to find out whether the UA you are sending\n  is the one you signed with.\n\n**Returns**\n\nOne entry per parameter found, each with `fields` (what is inside),\n`checks` (whether your candidate inputs are the ones it sealed, with the\nexact `covered` string when they are) and `notes` (internal checksums this\nendpoint recomputed).\n\nA `checks` entry is `match`, `differs`, or `not_supplied` - and the third is\nnot the second. Nothing is reported as wrong because you did not send it.\n\nFields are labelled, not translated: `name` is a stable slug, so a client\nmay key off it.","operationId":"decode_api_v1_tools_decode_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecodeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"tools_decode","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tools/parse-url":{"get":{"tags":["tools"],"summary":"Identify a share link","description":"Work out what a link points at, without fetching anything.\n\nThis is the id extraction v4 published as a dozen `get_*_id` routes, as one\nendpoint: it reports the platform, what kind of resource the link names, and\nthe id that names it.\n\nWhich id that is differs by platform, because the endpoints differ. A Douyin\nprofile link yields a `sec_user_id`; a TikTok one yields the `@handle`,\nbecause that is what TikTok's user-detail call accepts. Both are returned\nunder `resource_id`, with `handle` alongside when there is one.\n\nA short link (`v.douyin.com`, `vm.tiktok.com`) cannot be resolved without\nfollowing it, which is a network call. Those come back with\n`needs_expansion: true` and no id; send them to `/parse` instead.\n\n**Parameters**\n\n- `url` - a share link, or the whole clipboard text with a link in it.\n\n**Returns**\n\nThe platform, resource kind, id, handle, canonical URL, whether it needs\nexpanding, and whether it is a supported target at all.","operationId":"parse_url_api_v1_tools_parse_url_get","parameters":[{"name":"url","in":"query","required":true,"schema":{"type":"string","maxLength":4096,"description":"A share link, or text with one inside it.","title":"Url"},"description":"A share link, or text with one inside it."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"tools_parse_url","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tools/parse-batch":{"post":{"tags":["tools"],"summary":"Identify many links and ids at once","description":"Work out what a list of links and ids point at, without fetching anything.\n\n`/tools/parse-url` answers one at a time, which is the wrong shape for the\nthing people actually have: a column pasted out of a spreadsheet, or the\noutput of a scrape. Nothing here touches the network, so a thousand lines\ncost one request and no identity.\n\nEach line is tried as a link first and as a bare post id second. An id is\nchecked against its own embedded timestamp - both platforms mint ids whose\nhigh 32 bits are the Unix second they were issued - so a typo is named as\none here instead of becoming an upstream request that spends an identity to\nbe told the same thing.\n\nThat check cannot tell a post that never existed from one that has been\ndeleted: a plausible id is a plausible id. It rejects text that cannot be an\nid at all, which is the part worth doing for free.\n\n**Parameters**\n\n- `text` - the lines, separated by newlines. Duplicates and blanks go.\n\n**Returns**\n\nOne entry per distinct line in the order given, each with what it was\nrecognised as, and a summary count by outcome.","operationId":"parse_batch_api_v1_tools_parse_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchParseRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"tools_parse_batch","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/tools/identity":{"post":{"tags":["tools"],"summary":"Mint a guest identity and return it","description":"Drive a real browser to the platform and hand back the cookies it was given.\n\nThis is the half of the work that cannot be computed. `msToken` on TikTok\nand `UIFID_TEMP` on Douyin are issued by the platform to a browser that\nloaded its page; no algorithm produces them, which is why a signature alone\nis not enough to make a request that works.\n\nExpect it to take tens of seconds: it launches a browser, loads the page and\nwaits for the scripts to finish setting cookies. It needs the\n`identity:manage` scope, which ordinary read keys do not carry.\n\nNothing is stored. The jar is returned to you and forgotten - use\n`/api/v1/admin/identities/mint` instead if you want it added to this\ninstance's own pool.\n\n**Parameters**\n\n- `platform` - `douyin` or `tiktok`.\n- `proxy` - mint behind this egress. Subject to the same\n  `security.request_proxy` setting as every other proxy parameter, and worth\n  setting: cookies minted at one address and used from another are the\n  mismatch both platforms look for.\n\n**Returns**\n\nThe cookie jar, the browser fingerprint it was minted with - User-Agent,\nplatform, screen, language, timezone - and the exit address it was seen\nfrom. Send all of it together; a jar used with a different User-Agent is a\nweaker identity than no identity.","operationId":"mint_identity_api_v1_tools_identity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"tools_identity","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities":{"get":{"tags":["admin"],"summary":"List identities with state and health","description":"The identity pool, newest first.\n\nCookies are never returned; this is the health view, not the credential.\n\n**Parameters**\n\n- `platform` - restrict to one platform.\n- `state` - restrict to one state, such as `active` or `cooling`.\n- `limit` - how many to return.\n\n**Returns**\n\nEach identity's id, platform, state, the proxy it is bound to - its\n`proxy_id` and `proxy_label`, the name someone gave that proxy, `null` when\nthere is no proxy or no name, and never its URL - when it was minted and\nlast used, and its health score over the last 15 and 60 minutes: `null` for\nan identity the aggregate has no traffic for, which is not the same as zero.","operationId":"list_identities_api_v1_admin_identities_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only identities for this platform.","title":"Platform"},"description":"Only identities for this platform."},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/IdentityState"},{"type":"null"}],"description":"Only identities in this state.","title":"State"},"description":"Only identities in this state."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum identities to return.","default":100,"title":"Limit"},"description":"Maximum identities to return."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/pool":{"get":{"tags":["admin"],"summary":"Pool level against its low-water mark","description":"What the refill job is looking at, per platform.\n\nThe job exists and has since the beginning - the worker checks each platform\nevery minute and mints one identity at a time until the pool is back at\n`pool.target_size` - but nothing on the identities board said so, so the\nonly way to know it was running was to watch rows appear. This is that\ncheck, answered on demand.\n\n`usable` is the number the job actually compares against the mark: live\nidentities whose consecutive failure streak is still under\n`pool.max_fail_streak`. It is deliberately not the row count. An identity\nthat fails every request stays live, so a pool counted by rows can sit at\nits target while serving nothing.\n\n`minting` is whether this deployment can mint at all. Without a browser\ncontainer there is nothing to mint with, the job is skipped entirely, and a\nlow-water mark is a number with no effect - which is worth saying out loud\nrather than leaving an operator to wonder why the pool never refills.\n\n`activity` is what the job has actually been doing: the mint in flight if\nthere is one, the last few attempts with their outcomes, and the backoff if\nrepeated failures have put the sweep to sleep. It comes from the worker via\nRedis rather than from this database, because a failed mint writes no row -\nwhich is exactly why a pool that stubbornly will not refill used to look\nidentical to one nobody asked to refill.","operationId":"pool_level_api_v1_admin_identities_pool_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_pool","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/mint":{"post":{"tags":["admin"],"summary":"Mint guest identities","description":"Queue a minting job per identity requested.\n\nMinting drives a real browser through the proxy, which takes seconds and is\nnot on the request path; it is queued like any other job (doc 02).","operationId":"mint_api_v1_admin_identities_mint_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintRequest"}}},"required":true},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_mint","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/import":{"post":{"tags":["admin"],"summary":"Import a logged-in cookie jar","description":"Detect the paste format, report what was understood, then store it.\n\n``dry_run`` returns the report without writing anything, which is what the\nconsole shows before the user commits: a mistyped jar is far easier to spot\nhere than in a week of failing requests.","operationId":"import_identity_api_v1_admin_identities_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityImport"}}},"required":true},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_import","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}/cookies":{"get":{"tags":["admin"],"summary":"What one identity's jar holds","description":"The names, roles and masked values of the cookies this identity holds.\n\nAn identity was a row of verdicts. When one stopped working the console\ncould say *that* the session was spent and never *what the jar contained*,\nso the next step was always the same: open a shell, decrypt the column by\nhand, and read it there. This is that step, done in the place the question\nwas asked.\n\n**The values stay masked, and that is not an oversight.** A cookie jar is\nthe credential - a logged-in one is somebody's account - and this module's\nrule is that no response carries one. What comes back instead is everything\nthat makes the jar diagnosable: which cookies are present, which of them\nthis build considers required, session-bearing or merely useful, how long\neach value is, and its first and last four characters, which is enough to\ntell two jars apart and useless to anyone who steals the response.\n\n`length` is here where ``GET /identities`` deliberately withholds it. On\nthat endpoint it would be a number per row on a listing polled every five\nseconds; here it is the answer to \"is this token truncated\", which is a\nreal failure and one that looks exactly like a wrong token from outside.\n\n**Returns**\n\nThe identity's platform and source, whether its jar carries a logged-in\nsession, and one entry per cookie: `name`, `role`, `masked`, `length`.","operationId":"identity_cookies_api_v1_admin_identities__identity_id__cookies_get","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to inspect.","title":"Identity Id"},"description":"The identity to inspect."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_cookies","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/import/bundle":{"post":{"tags":["admin"],"summary":"Import an export document","description":"Restore identities from a file this build exported.\n\nThe other import takes a paste, which is what somebody has when they copy a\njar out of a browser. This takes what somebody has when they exported a\npool: many identities at once, each already carrying the fingerprint it was\nminted or imported with.\n\nCarrying the fingerprint is the point. Re-pasting an exported jar through\nthe paste route infers the browser from a user agent that is no longer\nbeside it, so a restored identity signs with a different fingerprint from\nthe one the platform first saw it with - which is exactly the difference\nrisk control is looking for.\n\nEvery entry is judged on its own. One jar that has expired since it was\nexported does not stop the rest: the response says what was stored, what\nwas skipped and why, and `dry_run` answers that question without writing.\n\n**Parameters**\n\n- `version` - the document's shape. A newer one is refused rather than\n  read on a guess.\n- `identities` - the entries, as `POST /identities/export` wrote them.\n- `proxy_id` - bind every restored identity to this egress. Optional.\n- `dry_run` - report without storing.\n\n**Returns**\n\n`stored`, and one result per entry: its platform, whether it was usable,\nand the id it was given.","operationId":"import_bundle_api_v1_admin_identities_import_bundle_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityBundle"}}},"required":true},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_import_bundle","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}/cookies/reveal":{"get":{"tags":["admin"],"summary":"Read one identity's cookies in full","description":"The jar as it will be sent, values and all.\n\nA deliberate exception to this module's rule, and the reason it is a route\nof its own rather than a flag on the inventory: the masked view is what the\ndrawer loads on every open and this is what somebody asks for on purpose.\nSeparating them means the audit line records the second and is not drowned\nby the first, and the wider scope - operator, not any admin reader - is\nenforced by the dependency rather than by a branch inside a handler.\n\nWhy it exists at all: an operator debugging a jar ends up comparing it\nagainst a browser's, and a mask cannot be compared. The alternative on\noffer was a shell and a hand-rolled decrypt, which is the same disclosure\nwith no audit line and no scope check in front of it.\n\n**Returns**\n\nThe cookies as a mapping, the same jar as one ``Cookie:`` header, and the\nexpiry read out of the session cookie where there is one.","operationId":"reveal_cookies_api_v1_admin_identities__identity_id__cookies_reveal_get","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to read.","title":"Identity Id"},"description":"The identity to read."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_cookies_reveal","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/export":{"post":{"tags":["admin"],"summary":"Export identities as a file","description":"Hand back whole identities - jars, fingerprints and all - as a document.\n\nThe console could import a jar and never get one back out, so an operator\nwho had built a working pool could not move it to a second instance, keep a\ncopy before a risky change, or hand one identity to somebody debugging.\nThe only route out was the database.\n\n**What comes back is a credential file.** Every jar in it works until the\nplatform expires it, and a logged-in one is somebody's account. It is\naudited by identity, and the document says so in a field the importer\nignores, so a person who finds one later knows what they are holding.\n\n**Parameters**\n\n- `identity_ids` - which identities. Between one and 200.\n\n**Returns**\n\nA document with a `version`, the export time, and one entry per identity.\nRetired identities export as an entry with an empty jar rather than being\ndropped: silently returning fewer than were asked for is how an operator\nfinds out mid-restore.","operationId":"export_identities_api_v1_admin_identities_export_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityExport"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_export","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}/test":{"post":{"tags":["admin"],"summary":"Probe one identity","description":"Queue a single real request on this identity.\n\nThe first thing anyone does while debugging is try one identity on its own.\nWithout this they have to send production traffic to find out, which is\nslow and pollutes the health statistics (doc 07).","operationId":"test_identity_api_v1_admin_identities__identity_id__test_post","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to act on.","title":"Identity Id"},"description":"The identity to act on."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_test","x-statuses":["202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}/session":{"post":{"tags":["admin"],"summary":"Check whether an identity's login is still valid","description":"Ask the platform whose session this identity is carrying.\n\n``authenticated`` on an identity is a statement about the paste it came\nfrom - a `sessionid` was present - and not about the account. The cookie is\nthere whether or not the platform still honours it, and the difference only\never surfaced later, as logged-in-only data quietly missing from otherwise\nsuccessful responses.\n\nThis asks the platform. Both publish an endpoint that answers about the\ncookies that asked rather than about a user named in the request, which is\nwhat makes it a login check and not a lookup.\n\nQueued rather than answered inline, like the probe beside it: it is a real\nupstream call with a real timeout.\n\n**Returns**\n\nA task id. Its result carries `logged_in`, the `account_id` the platform\nnamed, and a `reason` - `live`, `expired`, `guest`, `refused` or\n`unreachable` - because \"the session is over\" and \"the platform said\nnothing\" send an operator to different places.","operationId":"check_session_api_v1_admin_identities__identity_id__session_post","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to check.","title":"Identity Id"},"description":"The identity to check."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_session","x-statuses":["202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}/reset":{"post":{"tags":["admin"],"summary":"Return an identity to rotation","description":"Clear a cooldown and a failure streak, and mark the identity active.\n\nThe pool recovers on its own and does so slowly on purpose: an identity that\nhas failed repeatedly usually deserves the probation. This is the override\nfor the case that recovery cannot know about - the failures were not the\nidentity's fault.\n\nThat case is real. Until 2026-09-09 this instance read Douyin's answer for a\npost that does not exist as risk control, so looking up one wrong id cooled\nthe identity that asked and added to its streak. Fixing the classifier stops\nit recurring and repairs none of the damage: a degraded identity waits out a\ncooldown it never earned, and a streak keeps it out of the pool level until\nthat many successes have gone by.\n\nRefused on a retired identity, whose cookie jar was wiped when it was\nretired: there is no session left to return to rotation, and a button that\nappeared to bring one back would be a lie.\n\n**Returns**\n\nThe identity's id and its new state.","operationId":"reset_identity_api_v1_admin_identities__identity_id__reset_post","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to act on.","title":"Identity Id"},"description":"The identity to act on."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_reset","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/identities/{identity_id}":{"delete":{"tags":["admin"],"summary":"Retire an identity","description":"Retire and wipe. The statistics stay, the credential does not (doc 05).","operationId":"retire_identity_api_v1_admin_identities__identity_id__delete","parameters":[{"name":"identity_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The identity to act on.","title":"Identity Id"},"description":"The identity to act on."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RetireRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"identities_retire","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/proxies":{"get":{"tags":["admin"],"summary":"List proxies","description":"Every configured egress, oldest first.\n\nCredentials in a proxy URL are masked; the full URL is never returned.\n\n**Parameters**\n\n- `healthy_only` - hide proxies that failed their last probe.\n\n**Returns**\n\nEach proxy's masked URL, label, country, timezone, health and how many\nidentities are bound to it.","operationId":"list_proxies_api_v1_admin_proxies_get","parameters":[{"name":"healthy_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only list proxies that passed their last probe.","default":false,"title":"Healthy Only"},"description":"Only list proxies that passed their last probe."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["admin"],"summary":"Add one proxy","description":"Add one egress to the pool.\n\nThe URL is stored encrypted and only ever returned masked. To add many at\nonce, use `/proxies/import` instead.\n\n**Parameters**\n\n- `url` - the proxy URL, `scheme://[user:pass@]host:port`.\n- `label` - an optional name to recognise it by.\n- `country`, `timezone` - optional overrides; otherwise they come from a\n  GeoIP lookup of the exit address when the proxy is probed.\n\n**Returns**\n\nThe stored proxy, with its URL masked.","operationId":"create_proxy_api_v1_admin_proxies_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyCreate"}}}},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_create","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/proxies/import":{"post":{"tags":["admin"],"summary":"Bulk import a pasted proxy list","description":"Accept a block of lines and report the fate of each one.\n\nPartial success is the point: a provider list with three bad rows should\nstill import the other ninety-seven (doc 07).","operationId":"import_proxies_api_v1_admin_proxies_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyImport"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_import","x-statuses":["200","201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/proxies/{proxy_id}":{"put":{"tags":["admin"],"summary":"Update one proxy","description":"Change one proxy's URL, label or geography.\n\nOnly the fields you send are changed. Replacing the URL re-encrypts it and\nleaves the identities bound to this proxy in place.\n\n**Parameters**\n\n- `proxy_id` - the proxy to update.\n- `url`, `label`, `country`, `timezone` - any subset of these.\n\n**Returns**\n\nThe updated proxy, with its URL masked.","operationId":"update_proxy_api_v1_admin_proxies__proxy_id__put","parameters":[{"name":"proxy_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The proxy to update.","title":"Proxy Id"},"description":"The proxy to update."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_update","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["admin"],"summary":"Delete a proxy and retire what used it","description":"Delete one egress, and retire every identity bound to it.\n\nAn identity is tied to the exit it was minted behind, so it cannot outlive\nthat exit: the identities go too, and their cookies are wiped. This cannot\nbe undone.\n\n**Parameters**\n\n- `proxy_id` - the proxy to delete.\n\n**Returns**\n\nHow many identities were retired along with it.","operationId":"delete_proxy_api_v1_admin_proxies__proxy_id__delete","parameters":[{"name":"proxy_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The proxy to delete.","title":"Proxy Id"},"description":"The proxy to delete."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_delete","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/proxies/{proxy_id}/test":{"post":{"tags":["admin"],"summary":"Probe one proxy","description":"Queue a connectivity and GeoIP probe for this egress.","operationId":"test_proxy_api_v1_admin_proxies__proxy_id__test_post","parameters":[{"name":"proxy_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The proxy to probe.","title":"Proxy Id"},"description":"The proxy to probe."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"proxies_test","x-statuses":["202"],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/api-keys":{"get":{"tags":["admin"],"summary":"List API keys","description":"Every API key on this instance, newest first.\n\nSecrets are never returned - a key's value is shown once, when it is\ncreated, and cannot be read back afterwards.\n\n**Parameters**\n\n- `mine_only` - list only the keys you created.\n\n**Returns**\n\nEach key's name, scopes, owner, creation and last-used times, and whether\nit is still active.","operationId":"list_keys_api_v1_admin_api_keys_get","parameters":[{"name":"mine_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only list keys you created, rather than everyone's.","default":false,"title":"Mine Only"},"description":"Only list keys you created, rather than everyone's."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"api_keys_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["admin"],"summary":"Create an API key","description":"Mint a key and show it once.\n\nThe response carries ``key`` and a warning that it will not be shown again;\nthe console has to put that in front of the user, because the only recovery\nfrom losing it is creating another one.","operationId":"create_key_api_v1_admin_api_keys_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}}},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"api_keys_create","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/api-keys/{key_id}":{"delete":{"tags":["admin"],"summary":"Revoke an API key","description":"Revoke one API key.\n\nTakes effect immediately and cannot be undone; issue a new key instead of\ntrying to restore this one.\n\n**Parameters**\n\n- `key_id` - the key to revoke.","operationId":"revoke_key_api_v1_admin_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The key to revoke.","title":"Key Id"},"description":"The key to revoke."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"api_keys_revoke","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/settings":{"get":{"tags":["admin"],"summary":"Read every runtime setting","description":"Every runtime setting, with its current value and where that came from.\n\nSecrets are redacted. A value can come from the database, the environment\nor the built-in default, and the response says which - so a setting that\nrefuses to change is explained rather than mysterious.\n\n**Returns**\n\nEach setting's key, current value, source, type, allowed choices where it\nis constrained, and a description in the requested language.","operationId":"list_settings_api_v1_admin_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"settings_list","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/settings/{key}":{"put":{"tags":["admin"],"summary":"Change one runtime setting","description":"Change one runtime setting; it takes effect without a restart.\n\nThe value is validated against the setting's type and, where it has one,\nits list of allowed choices. Settings that can disrupt a running instance\nrequire `confirm`.\n\n**Parameters**\n\n- `key` - the setting to change.\n- `value` - the new value, in the setting's own type.\n- `confirm` - required for settings flagged as disruptive.\n\n**Returns**\n\nThe setting, with its new value and source.","operationId":"update_setting_api_v1_admin_settings__key__put","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","description":"The setting to change, as shown by the list endpoint.","title":"Key"},"description":"The setting to change, as shown by the list endpoint."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SettingUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"settings_update","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["admin"],"summary":"Reset one setting to its inherited value","description":"Delete the override so the key falls back to the environment or default.","operationId":"reset_setting_api_v1_admin_settings__key__delete","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","description":"The setting to reset, as shown by the list endpoint.","title":"Key"},"description":"The setting to reset, as shown by the list endpoint."},{"name":"confirm","in":"query","required":false,"schema":{"type":"boolean","description":"Required for settings flagged as disruptive.","default":false,"title":"Confirm"},"description":"Required for settings flagged as disruptive."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"settings_reset","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/demo":{"get":{"tags":["admin"],"summary":"Show the demo account","description":"What this instance publishes as its demo, without the secrets.\n\n**Returns**\n\nWhether demo mode is on, whether an account exists, the account name, the\nAPI key prefix and the key's scopes. Never the password and never the key:\nneither is stored in a form this endpoint could return, so a lost one is\nrotated rather than looked up.","operationId":"show_demo_api_v1_admin_demo_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"demo_show","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/demo/rotate":{"post":{"tags":["admin"],"summary":"Reissue the demo credentials","description":"Generate a new demo password and API key, replacing the current pair.\n\nThe previous key is revoked rather than deleted, so the task and request\nrows that reference it keep meaning something. Every open demo session is\nended too: the point of rotating is usually that the old password reached\nsomebody it should not have, and leaving their session alive would make the\nrotation cosmetic.\n\n**Returns**\n\nThe new password and the new API key, in the clear and for the only time.","operationId":"rotate_demo_api_v1_admin_demo_rotate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"demo_rotate","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/users":{"get":{"tags":["admin"],"summary":"List console accounts","description":"Every console account.\n\nPassword hashes are never returned.\n\n**Returns**\n\nEach account's id, username, role and when it was created.","operationId":"list_users_api_v1_admin_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"users_list","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["admin"],"summary":"Create a console account","description":"Create a console account.\n\nThe password is checked against the instance's strength policy and stored\nhashed. Usernames are unique.\n\n**Parameters**\n\n- `username` - the new account name.\n- `password` - its password.\n- `role` - what the account may do.\n\n**Returns**\n\nThe created account, without its password.","operationId":"create_user_api_v1_admin_users_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreate"}}},"required":true},"responses":{"201":{"description":"Created. `data` is the new resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"users_create","x-statuses":["201"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/users/{user_id}":{"put":{"tags":["admin"],"summary":"Change a role or reset a password","description":"The password reset an administrator performs for someone else.\n\nChanging your own password goes through ``/api/v1/auth/password``, which\ndemands the current one; that check is meaningless here and would only\nstop an administrator from helping a user who forgot theirs.","operationId":"update_user_api_v1_admin_users__user_id__put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The account to change.","title":"User Id"},"description":"The account to change."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"users_update","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["admin"],"summary":"Delete a console account","description":"Delete a console account and revoke its sessions.\n\nYou cannot delete the account you are signed in with, and the last\nremaining administrator cannot be deleted either.\n\n**Parameters**\n\n- `user_id` - the account to delete.","operationId":"delete_user_api_v1_admin_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The account to delete.","title":"User Id"},"description":"The account to delete."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"users_delete","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/endpoints/health":{"get":{"tags":["admin"],"summary":"Per-endpoint health and circuit state","description":"Every declared endpoint, whether or not it has traffic.\n\nListing the quiet ones too is deliberate: V4's failure mode was an endpoint\nthat quietly died and nobody noticed, and an endpoint missing from a board\nis exactly as invisible as one that was never called.","operationId":"endpoint_health_api_v1_admin_endpoints_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"endpoints_health","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/metrics/timeseries":{"get":{"tags":["admin"],"summary":"Request volume and outcomes over time","description":"Raw buckets. Series selection and zooming happen in the browser (doc 07).","operationId":"metrics_timeseries_api_v1_admin_metrics_timeseries_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"How far back the window reaches.","default":24,"title":"Hours"},"description":"How far back the window reaches."},{"name":"step","in":"query","required":false,"schema":{"type":"integer","maximum":86400,"minimum":60,"description":"Bucket width in seconds.","default":300,"title":"Step"},"description":"Bucket width in seconds."},{"name":"endpoint","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only traffic to this endpoint.","title":"Endpoint"},"description":"Only traffic to this endpoint."},{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only this platform.","title":"Platform"},"description":"Only this platform."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"metrics_timeseries","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/endpoints/access":{"get":{"tags":["admin"],"summary":"Which endpoints are served without a key","description":"Every endpoint, with whether it currently needs a credential.\n\nThe list is built from the running route table, so it cannot drift from what\nthe API actually serves.\n\n**Returns**\n\nOne row per operation: its method and path, the summary shown in the API\ndocument, whether it is currently open, and whether it is permanently\nprotected. Admin, authentication and setup endpoints are always protected\nand can never be opened.","operationId":"endpoint_access_api_v1_admin_endpoints_access_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"endpoints_access","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/logs/requests":{"get":{"tags":["admin"],"summary":"Read the request log","description":"Recent requests, newest first, filtered.\n\n``minutes`` and ``limit`` are both bounded by FastAPI before the handler\nruns, so there is no combination of parameters that reaches the hypertable\nwithout a window: a caller asking for a million rows is rejected rather\nthan served a million rows or silently given a hundred.\n\nThe consequence worth knowing is that a ``request_id`` older than the\nwindow is not found. Widening the range is the answer; dropping the bound\nfor one lookup would leave the expensive path one query string away.\n\n``outcome`` repeats. Unknown values are rejected by the ``Outcome``\nannotation rather than ignored, because a filter that silently matches\neverything reads exactly like a filter that found everything.","operationId":"list_request_log_api_v1_admin_logs_requests_get","parameters":[{"name":"request_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Only the request with this id.","title":"Request Id"},"description":"Only the request with this id."},{"name":"endpoint","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only requests to this endpoint.","title":"Endpoint"},"description":"Only requests to this endpoint."},{"name":"identity_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Only requests made with this identity.","title":"Identity Id"},"description":"Only requests made with this identity."},{"name":"outcome","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Outcome"}},{"type":"null"}],"description":"Only these outcomes. Repeat the parameter to allow several.","title":"Outcome"},"description":"Only these outcomes. Repeat the parameter to allow several."},{"name":"minutes","in":"query","required":false,"schema":{"type":"integer","maximum":43200,"minimum":1,"description":"How far back to look. Rows older than the log's retention are gone.","default":60,"title":"Minutes"},"description":"How far back to look. Rows older than the log's retention are gone."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum rows to return.","default":100,"title":"Limit"},"description":"Maximum rows to return."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"request_log","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/audit":{"get":{"tags":["admin"],"summary":"Read the audit trail","description":"Sensitive operations, newest first.\n\nKept apart from ``request_log`` on purpose: this is the record of who\nchanged what, and it must not be lost in the volume of ordinary traffic\n(doc 08).","operationId":"list_audit_api_v1_admin_audit_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum rows to return.","default":100,"title":"Limit"},"description":"Maximum rows to return."},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"description":"Only this action, such as `settings.update`.","title":"Action"},"description":"Only this action, such as `settings.update`."},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only entries older than this timestamp. Page with it.","title":"Before"},"description":"Only entries older than this timestamp. Page with it."},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Only what this console account did.","title":"User Id"},"description":"Only what this console account did."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"audit_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/diagnose":{"post":{"tags":["admin"],"summary":"Run the six-step self check","description":"Queue the same diagnosis the setup wizard finishes with (doc 15).\n\nThe report it produces is redacted at the source; proxy passwords, cookies\nand API keys never reach it, because its whole purpose is to be pasted into\na public issue.","operationId":"diagnose_api_v1_admin_diagnose_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/DiagnoseRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"diagnose","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/notifications/test":{"post":{"tags":["admin"],"summary":"Send a test alert","description":"Prove the channel works before an incident depends on it.","operationId":"test_notification_api_v1_admin_notifications_test_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/NotificationTest"},{"type":"null"}],"title":"Body"}}}},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"notify_test","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/backup":{"get":{"tags":["admin"],"summary":"List backup archives","description":"Archives the server can see, newest first.\n\nAn archive that cannot be parsed is listed with its reason rather than\ndropped, and one bad file never fails the listing: a corrupt archive is the\nsingle most important thing this page has to be able to show, because it is\nthe reason someone opens it (doc 15).\n\nRead at the same level as the rest of the administrative surface, one step\nbelow creating and restoring. Nothing here is a credential - names, sizes,\nrow counts and format versions - and an operator who cannot take a backup\nstill has to be able to see whether last night's ran.","operationId":"list_backup_archives_api_v1_admin_backup_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"backup_list","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["admin"],"summary":"Create a backup archive","description":"Queue an export.\n\nIdentities are excluded unless asked for: they are bound to an egress and a\nfingerprint, so reviving them on another machine means using cookies from a\nnew exit address, which is precisely what doc 02 forbids. Credentials are\nexported as ciphertext and the archive never contains the master key, so a\nleaked backup is not a leaked cookie jar (doc 15).","operationId":"create_backup_api_v1_admin_backup_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BackupRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"backup_create","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/backup/restore":{"post":{"tags":["admin"],"summary":"Restore a backup archive","description":"Queue a restore of one archive.\n\nAdministrators only, and confirmed: the archive carries users, API keys and\nsettings, so restoring one can hand an account back its access, and nothing\nin the console undoes it. That is the same bar a SENSITIVE setting write\nclears in ``admin/settings.py`` - the role, the scope and an explicit\n``confirm`` - and it leaves the same audit row behind. Rows that already\nexist are kept, so this fills the gaps in a live instance rather than\nreverting it.\n\nThe archive is vetted here, before anything is queued, because all three\nways it can be unusable are things the caller has to be told about while\nthey are still looking at the dialog: no such file, a format this build\ncannot read, and - the one that surprises people - an archive written under\na different DTK_SECRET_KEY, whose ciphertext this instance could store but\nnever decrypt. The worker checks all three again; between queueing and\nrunning, a file can change.","operationId":"restore_backup_archive_api_v1_admin_backup_restore_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestoreRequest"}}},"required":true},"responses":{"202":{"description":"Accepted and queued. `data.task_id` names the task to poll.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"backup_restore","x-statuses":["202"],"parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/watchlist":{"get":{"tags":["admin"],"summary":"List watched targets","description":"Every target this instance collects on a timer, soonest due first.","operationId":"list_watchlist_api_v1_admin_watchlist_get","parameters":[{"name":"platform","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Only this platform.","title":"Platform"},"description":"Only this platform."},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/WatchKind"},{"type":"null"}],"description":"Only entries of this kind.","title":"Kind"},"description":"Only entries of this kind."},{"name":"enabled","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Only enabled entries, or only paused ones.","title":"Enabled"},"description":"Only enabled entries, or only paused ones."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":500,"minimum":1},{"type":"null"}],"description":"Rows per page.","title":"Limit"},"description":"Rows per page."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Rows to skip.","default":0,"title":"Offset"},"description":"Rows to skip."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"watchlist_list","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"post":{"tags":["admin"],"summary":"Watch a target","description":"Start collecting one author or post on a schedule.\n\nDue immediately: someone who just added an author wants to see it collect,\nand holding the first run back by the interval would only make the feature\nlook broken for six hours.","operationId":"add_watch_api_v1_admin_watchlist_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"watchlist_create","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/watchlist/{entry_id}":{"patch":{"tags":["admin"],"summary":"Change or pause a watched target","description":"Change the interval, the page depth, or whether it runs at all.\n\nRe-enabling clears the failure backoff: the operator is asserting that\nwhatever was wrong is fixed, and making them wait out an eight-hour backoff\nto find out would be its own bug.","operationId":"update_watch_api_v1_admin_watchlist__entry_id__patch","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The watchlist entry id.","title":"Entry Id"},"description":"The watchlist entry id."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"watchlist_update","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]},"delete":{"tags":["admin"],"summary":"Stop watching a target","description":"Stop collecting this target.\n\nRemoves the schedule and nothing else. Everything already collected stays\nin the archive and in the snapshot history - deleting the instruction is\nnot a request to delete what it produced.","operationId":"remove_watch_api_v1_admin_watchlist__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","description":"The watchlist entry id.","title":"Entry Id"},"description":"The watchlist entry id."},{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"watchlist_delete","security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}},"/api/v1/admin/watchlist/pause":{"post":{"tags":["admin"],"summary":"Pause every watched target","description":"Stop all scheduled collection without losing the list.\n\nThe button for \"something is wrong and I want the pool back\". Turning the\n`watchlist.enabled` setting off does the same thing globally; this leaves\nthat setting alone and pauses the entries themselves, so re-enabling is\nper entry and deliberate.","operationId":"pause_watchlist_api_v1_admin_watchlist_pause_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"400":{"description":"The request was rejected. `error.code` says why.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"401":{"description":"No API key or session, or it is not valid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"403":{"description":"Authenticated, but this credential lacks the scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"404":{"description":"No such resource, or the platform says the content is gone.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"429":{"description":"Rate limited. `error.retry_after` says when to come back.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"500":{"description":"An unhandled error. `meta.request_id` identifies it in the logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"502":{"description":"The platform refused, withheld the payload, or changed shape.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}},"503":{"description":"The identity pool, the queue or an upstream endpoint is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DtkResponse"}}}}},"x-i18n":"watchlist_pause","parameters":[{"name":"lang","in":"query","required":false,"description":"Response language for human-readable text. One of en or zh; anything else falls back to en.","schema":{"type":"string","enum":["en","zh"]}}],"security":[{"ApiKeyAuth":[]},{"SessionCookie":[]}]}}},"components":{"schemas":{"ApiKeyCreate":{"properties":{"name":{"type":"string","maxLength":128,"minLength":1,"title":"Name","description":"What the key is for. Shown in the list and in the audit log."},"scopes":{"items":{"$ref":"#/components/schemas/Scope"},"type":"array","title":"Scopes","description":"What the key may do. A key can never be given a scope its creator does not hold, so this is bounded by your own."},"rate_limit":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":1.0},{"type":"null"}],"title":"Rate Limit","description":"Requests per minute. Null means the instance default. Abuse protection only - this project has no billing."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"When the key stops working. Must be in the future; null never expires."}},"additionalProperties":false,"type":"object","required":["name"],"title":"ApiKeyCreate"},"ArchiveDelete":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ContentRef"},"type":"array","maxItems":500,"minItems":1,"title":"Items","description":"The posts to act on. Past this limit make two calls rather than hold one transaction open across the archive."},"media":{"type":"boolean","title":"Media","description":"Also delete the stored media files. True because that is what \"delete this\" means when the thing on screen is a video on disk; false keeps the files and removes only the record.","default":true}},"additionalProperties":false,"type":"object","required":["items"],"title":"ArchiveDelete","description":"Remove posts from the archive, and optionally their stored media.\n\n`media` defaults to true because that is what \"delete this\" means when the\nthing on screen is a video the instance has on disk. Setting it false keeps\nthe files and removes only the record, which is the narrower thing somebody\nmight want and is not the obvious reading of the button."},"Availability":{"type":"string","enum":["live","deleted","private","unknown"],"title":"Availability","description":"Whether the archived post is still reachable upstream.\n\n``UNKNOWN`` is what a failed check leaves behind. It is deliberately not\n``DELETED``: an archive that quietly reclassifies posts on a network blip\nwould be worse than one that admits it does not know."},"BackfillRequest":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform the author is on."},"author_id":{"type":"string","maxLength":128,"minLength":1,"title":"Author Id","description":"The author's stable id - `sec_user_id` on Douyin, `secUid` on TikTok - and not the handle, which users edit."},"pages":{"type":"integer","maximum":20.0,"minimum":1.0,"title":"Pages","description":"How many pages of their posts to walk. Each page is one upstream request.","default":5}},"additionalProperties":false,"type":"object","required":["platform","author_id"],"title":"BackfillRequest"},"BackupRequest":{"properties":{"include_identities":{"type":"boolean","title":"Include Identities","description":"Include the identity pool and its cookie jars. Off by default: an identity is bound to one exit and one fingerprint, so restoring it onto another machine revives a credential that must not be used from a new address.","default":false}},"additionalProperties":false,"type":"object","title":"BackupRequest"},"BatchItem":{"properties":{"url":{"type":"string","maxLength":4096,"minLength":1,"title":"Url","description":"One link, in any of the forms `/parse` accepts."},"include_raw":{"type":"boolean","title":"Include Raw","description":"Also return this item's unparsed platform response.","default":false}},"additionalProperties":false,"type":"object","required":["url"],"title":"BatchItem"},"BatchParseRequest":{"properties":{"text":{"type":"string","maxLength":524288,"title":"Text","description":"Links, post ids, or whole share-sheet paste, one item per line. Blank lines and duplicates are dropped."}},"additionalProperties":false,"type":"object","required":["text"],"title":"BatchParseRequest","description":"A blob of text with links and ids in it, one per line."},"BatchRequest":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BatchItem"},"type":"array","maxItems":50,"minItems":1,"title":"Items","description":"The links to submit. Each becomes its own task with its own id and its own fate, so one bad link cannot smear the whole request into a single error."},"callback_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Callback Url","description":"Where to POST each result as it finishes. Allowlisted hosts only."}},"additionalProperties":false,"type":"object","required":["items"],"title":"BatchRequest"},"CollectionCreate":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name","description":"What to call it. Unique case-insensitively, enforced by the database."},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note","description":"Free text, for your own use."}},"additionalProperties":false,"type":"object","required":["name"],"title":"CollectionCreate","description":"A named set of posts, made by hand.\n\nNothing infers membership; that is the point of it. The name is unique\ncase-insensitively, which the database enforces rather than this schema -\na check here would still lose a race between two console tabs."},"CollectionUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":80,"minLength":1},{"type":"null"}],"title":"Name","description":"Rename it. Omit the field entirely to leave the name alone."},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note","description":"Replace the note. Omitting the field leaves it alone; sending null clears it - the two are told apart, so nothing is erased that nobody mentioned."}},"additionalProperties":false,"type":"object","title":"CollectionUpdate","description":"Rename a collection, change its note, or both.\n\nBoth fields are optional and both default to None, which is why the handler\nreads `model_fields_set` rather than the values: \"leave the note alone\" and\n\"clear the note\" arrive identically otherwise, and guessing at that would\nquietly erase text nobody mentioned."},"ContentKind":{"type":"string","enum":["video","image_album","live"],"title":"ContentKind"},"ContentRef":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform the post is on."},"content_id":{"type":"string","maxLength":128,"minLength":1,"title":"Content Id","description":"`aweme_id` or its TikTok equivalent. Text, never an integer: a 19-digit id exceeds the JavaScript safe range and a JSON parser would round it."}},"additionalProperties":false,"type":"object","required":["platform","content_id"],"title":"ContentRef","description":"One archived post, by the pair the archive is keyed on."},"ContentSelection":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ContentRef"},"type":"array","maxItems":500,"minItems":1,"title":"Items","description":"The posts to act on. Past this limit make two calls rather than hold one transaction open across the archive."}},"additionalProperties":false,"type":"object","required":["items"],"title":"ContentSelection","description":"The posts a bulk call acts on.\n\nBounded here rather than in the handler: past this a caller should make two\ncalls, not hold one transaction open across the archive."},"DecodeRequest":{"properties":{"value":{"type":"string","maxLength":8192,"title":"Value","description":"A signed URL, a query string, a `name=value` pair, or one parameter's value on its own."},"parameter":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Parameter","description":"Which parameter the value is, when its shape is ambiguous."},"user_agent":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"User Agent","description":"A candidate User-Agent, to check against the one that was signed."}},"additionalProperties":false,"type":"object","required":["value"],"title":"DecodeRequest","description":"One thing to take apart."},"DedupeRequest":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run","description":"Report what would go without deleting anything.","default":false}},"additionalProperties":false,"type":"object","title":"DedupeRequest","description":"Remove the older duplicate downloads of the same post.\n\nRe-downloading a post is a normal thing to do - the first attempt failed,\nthe mirrors went stale, the file was evicted - and each one leaves a row.\nWhat accumulates is several complete copies of the same video under the\nsame directory, which is the disk filling up with the same bytes."},"DiagnoseRequest":{"properties":{"include_smoke_test":{"type":"boolean","title":"Include Smoke Test","description":"Run the end-to-end step, which fetches one fixed public link and therefore spends an identity. Skipping it keeps the whole diagnosis local.","default":true}},"additionalProperties":false,"type":"object","title":"DiagnoseRequest"},"DownloadRequest":{"properties":{"platform":{"anyOf":[{"$ref":"#/components/schemas/Platform"},{"type":"null"}],"description":"Which platform the post is on. Implied by `url` when given."},"content_id":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Content Id","description":"The post id, as the archive holds it. Text, never an integer: a 19-digit id exceeds the JavaScript safe range."},"url":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"title":"Url","description":"A share link, or the share text with one inside it. Only the post id it yields is used; nothing here decides which host gets contacted."},"skip_existing":{"type":"boolean","title":"Skip Existing","description":"Return the existing download instead of fetching the post again, when this instance already holds its media. What re-running a feed wants: three new posts and forty already on disk.","default":false}},"additionalProperties":false,"type":"object","title":"DownloadRequest","description":"Start storing one post's media.\n\nA content key or a link, and a link is still not a URL the downloader will\nfetch. Doc 08's fourth constraint is that nothing a caller sends decides\nwhat host gets contacted, and that holds: a link here is run through\n`dtk.urls.identify`, which is the SSRF chokepoint, and only the post id it\nyields is used. The request goes to the platform's own endpoint table, the\nsame as if the id had been typed.\n\nEverything is optional at this layer because the combinations are the\nhandler's to judge - a link alone, a platform and an id, or a link whose\nplatform disagrees with the one named."},"DownloadState":{"type":"string","enum":["queued","running","done","partial","failed","cancelled"],"title":"DownloadState","description":"Where one media download stands.\n\nWider than :class:`TaskState`: a download of a post with several files can\nland some and lose others, which is neither done nor failed."},"DurationBucket":{"type":"string","enum":["short","medium","long","unknown"],"title":"DurationBucket","description":"Coarse length classes, from ``archive.duration_bucket``."},"IdentityBundle":{"properties":{"version":{"type":"integer","title":"Version","description":"The export document's format version. Checked rather than trusted: a file from a newer build may carry fields this one would drop silently."},"identities":{"items":{"additionalProperties":true,"type":"object"},"type":"array","maxItems":200,"minItems":1,"title":"Identities","description":"The `identities` array from the export document, unchanged."},"proxy_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Id","description":"Bind every imported identity to this exit."},"dry_run":{"type":"boolean","title":"Dry Run","description":"Report what the file holds without storing anything.","default":false}},"additionalProperties":false,"type":"object","required":["version","identities"],"title":"IdentityBundle","description":"An export document, on its way back in.\n\n`version` is checked rather than trusted: a file from a future build may\ndescribe fields this one would drop silently, and dropping a fingerprint\nturns a working identity into one the platform can tell apart from the\nbrowser it was taken from."},"IdentityExport":{"properties":{"identity_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"minItems":1,"title":"Identity Ids","description":"The identities to write out. The document carries every jar in full, so treat it as a credential file."}},"additionalProperties":false,"type":"object","required":["identity_ids"],"title":"IdentityExport","description":"Which identities to write into an export document.\n\nBounded at 200 because the response carries every jar in full and is\nassembled in memory; a pool larger than that is exported in pages, which is\na request the caller can see the size of."},"IdentityImport":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform the jar belongs to."},"cookies":{"type":"string","maxLength":200000,"minLength":1,"title":"Cookies","description":"The jar, in any of the shapes people actually hold: a `Cookie:` header, DevTools JSON, a cookies.txt file, or `name=value` pairs. The format is detected rather than declared."},"user_agent":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"User Agent","description":"The User-Agent of the browser the jar came from. Both platforms hash it into their signatures, so a jar sent under a different one is a contradiction they can see."},"language":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Language","description":"That browser's `navigator.language`, e.g. `zh-CN`."},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Timezone","description":"That browser's IANA zone, e.g. `Asia/Shanghai`. Should agree with the exit."},"proxy_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Id","description":"Send as this identity only through this exit."},"dry_run":{"type":"boolean","title":"Dry Run","description":"Report what was understood without storing anything. A mistyped cookie jar is far easier to spot than to debug.","default":false}},"additionalProperties":false,"type":"object","required":["platform","cookies"],"title":"IdentityImport","description":"Paste from DevTools, a cookie extension, cookies.txt or by hand.\n\nThe format is detected rather than declared: doc 07 is explicit that asking\nthe user which of four shapes they hold is how an import flow fails."},"IdentityRequest":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform to mint a guest session for."},"proxy":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Proxy","description":"Mint behind this egress, so the jar matches the address you will use it from."}},"additionalProperties":false,"type":"object","required":["platform"],"title":"IdentityRequest","description":"One guest identity to mint and hand back."},"IdentityState":{"type":"string","enum":["minting","active","cooling","degraded","retired"],"title":"IdentityState"},"LoginRequest":{"properties":{"username":{"type":"string","maxLength":128,"minLength":1,"title":"Username","description":"The console account."},"password":{"type":"string","maxLength":256,"minLength":1,"title":"Password","description":"Its password. A wrong one is rate limited per account and per address."}},"additionalProperties":false,"type":"object","required":["username","password"],"title":"LoginRequest"},"MintRequest":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform the new identities are for."},"count":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Count","description":"How many to mint. Each one drives a real headless browser against the platform, so this takes tens of seconds per identity.","default":1},"proxy_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Proxy Id","description":"Mint behind this exit and bind the identities to it. An identity is minted from one address and must keep using it; omitting this mints direct."}},"additionalProperties":false,"type":"object","required":["platform"],"title":"MintRequest"},"NotificationTest":{"properties":{"channel":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Channel","description":"Which configured channel to send a test through. Omitted sends through every channel that is configured."}},"additionalProperties":false,"type":"object","title":"NotificationTest"},"Outcome":{"type":"string","enum":["ok","business_error","risk_control","network_error"],"title":"Outcome","description":"Classification of a single upstream request.\n\nThe split between BUSINESS_ERROR and RISK_CONTROL is load bearing: a deleted\nvideo must never be counted against an identity's health."},"ParseRequest":{"properties":{"url":{"type":"string","maxLength":4096,"minLength":1,"title":"Url","description":"A share link, a bare post URL, or the whole clipboard text a platform app produces with the link buried in it. Short links are followed."},"include_raw":{"type":"boolean","title":"Include Raw","description":"Also return the platform's own response, unparsed. Large - a single post runs to hundreds of kilobytes - so it is off unless asked for.","default":false},"callback_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Callback Url","description":"Where to POST the result when the task finishes, instead of polling for it. The host must be on the operator's `security.url_allowlist`."}},"additionalProperties":false,"type":"object","required":["url"],"title":"ParseRequest","description":"A link, or the share text a platform app puts on the clipboard."},"PasswordChange":{"properties":{"current_password":{"type":"string","maxLength":256,"minLength":1,"title":"Current Password","description":"The password in use now. Checked even for an administrator changing their own."},"new_password":{"type":"string","maxLength":256,"minLength":8,"title":"New Password","description":"The replacement. Every other session for this account is signed out."}},"additionalProperties":false,"type":"object","required":["current_password","new_password"],"title":"PasswordChange"},"PinRequest":{"properties":{"pinned":{"type":"boolean","title":"Pinned","description":"True exempts this download from the size-based cleanup for good; false puts it back in scope.","default":true}},"additionalProperties":false,"type":"object","title":"PinRequest"},"Platform":{"type":"string","enum":["douyin","tiktok"],"title":"Platform"},"ProxyCreate":{"properties":{"url":{"type":"string","maxLength":2048,"minLength":3,"title":"Url","description":"The exit, as `scheme://user:pass@host:port`. Stored encrypted and never returned in full."},"label":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Label","description":"What to call it in the console."},"country":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Country","description":"ISO country code of the exit. Used to give an identity minted behind it a coherent locale; measured on the first probe when omitted."},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Timezone","description":"IANA zone of the exit, for the same reason as `country`."}},"additionalProperties":false,"type":"object","required":["url"],"title":"ProxyCreate"},"ProxyImport":{"properties":{"text":{"type":"string","maxLength":200000,"minLength":1,"title":"Text","description":"The proxies, one per line, in any of the common shapes - `host:port:user:pass`, `user:pass@host:port`, or a full URL."},"label":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Label","description":"Applied to every proxy in the paste, numbered when there is more than one."}},"additionalProperties":false,"type":"object","required":["text"],"title":"ProxyImport","description":"Several dozen lines pasted at once, in any of the common shapes."},"ProxyUpdate":{"properties":{"url":{"anyOf":[{"type":"string","maxLength":2048,"minLength":3},{"type":"null"}],"title":"Url","description":"Replace the exit address."},"label":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Label","description":"Rename it."},"country":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Country","description":"Correct the country."},"timezone":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Timezone","description":"Correct the zone."},"healthy":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Healthy","description":"Override what the last probe concluded. For an exit you know is fine but that fails the probe, or the reverse."}},"additionalProperties":false,"type":"object","title":"ProxyUpdate"},"RecheckRequest":{"properties":{"limit":{"anyOf":[{"type":"integer","maximum":200.0,"minimum":1.0},{"type":"null"}],"title":"Limit","description":"How many posts to check. Every one is a real request through the identity pool, so the batch is bounded here rather than left to the caller."},"older_than_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":0.0},{"type":"null"}],"title":"Older Than Days","description":"Only re-check posts last seen at least this many days ago."}},"additionalProperties":false,"type":"object","title":"RecheckRequest","description":"Verify that archived posts still exist.\n\nEvery post checked is a real request through the identity pool, so the\nbatch is bounded here rather than left to whatever the caller asks for."},"RestoreRequest":{"properties":{"path":{"type":"string","maxLength":255,"minLength":1,"title":"Path","description":"The file name the backup listing returned - a name, never a path. The server joins it onto the backup directory, so anything carrying a directory is refused rather than resolved."},"confirm":{"type":"boolean","title":"Confirm","description":"Required. A restore inserts archived users, API keys and settings into a live instance and cannot be undone from the console.","default":false}},"additionalProperties":false,"type":"object","required":["path"],"title":"RestoreRequest"},"RetireRequest":{"properties":{"reason":{"type":"string","maxLength":256,"title":"Reason","description":"Recorded on the identity's timeline, so a later reader knows why it went.","default":"retired from the console"}},"additionalProperties":false,"type":"object","title":"RetireRequest"},"RetryRequest":{"properties":{"download_id":{"type":"string","format":"uuid","title":"Download Id","description":"The download to start over, from the download list."}},"additionalProperties":false,"type":"object","required":["download_id"],"title":"RetryRequest","description":"Start a settled download over.\n\nThere is no resume to ask for. The sidecar truncates its `.part` on every\nattempt, and it is built that way because media URLs are signed and expire -\nbytes fetched an hour ago cannot be continued against a link that now\nanswers 403."},"Scope":{"type":"string","enum":["douyin:read","tiktok:read","identity:manage","archive:read","archive:export","media:read","media:write","admin"],"title":"Scope","description":"API key scopes."},"SettingUpdate":{"properties":{"value":{"title":"Value","description":"The new value, typed as the setting declares it. A string where a number is expected is refused rather than coerced."},"confirm":{"type":"boolean","title":"Confirm","description":"Required for a setting flagged SENSITIVE. Widening the URL allowlist or turning on the download proxy must not be a stray click.","default":false}},"additionalProperties":false,"type":"object","required":["value"],"title":"SettingUpdate"},"SetupInit":{"properties":{"token":{"type":"string","maxLength":256,"minLength":8,"title":"Token","description":"The setup token this instance printed to its log on first start. It is spent once and never issued again."},"username":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{2,63}$","title":"Username","description":"Username for the first administrator. Letters, digits, dot, dash, underscore."},"password":{"type":"string","maxLength":256,"minLength":8,"title":"Password","description":"Password for that account. Stored hashed and never returned."}},"additionalProperties":false,"type":"object","required":["token","username","password"],"title":"SetupInit"},"SignRequest":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Whose algorithm to sign with."},"url":{"type":"string","maxLength":4096,"title":"Url","description":"The API URL to sign, query included."},"user_agent":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"User Agent","description":"The User-Agent the request will be sent with."},"ms_token":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Ms Token","description":"TikTok session token to seal into the signature."},"cookies":{"anyOf":[{"type":"string","maxLength":8192},{"type":"null"}],"title":"Cookies","description":"The jar the request will be sent with, in any paste format. Douyin's own signature is computed over the visitor id inside it, so without it the signature headers cannot be produced."}},"additionalProperties":false,"type":"object","required":["platform","url"],"title":"SignRequest","description":"One request to sign."},"UserCreate":{"properties":{"username":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._-]{2,63}$","title":"Username","description":"Letters, digits, dot, dash and underscore. Unique, and not renameable."},"password":{"type":"string","maxLength":256,"minLength":8,"title":"Password","description":"Their initial password. Stored hashed and never returned."},"role":{"$ref":"#/components/schemas/UserRole","description":"What the account may do. You cannot create an account with a role above your own.","default":"viewer"}},"additionalProperties":false,"type":"object","required":["username","password"],"title":"UserCreate"},"UserRole":{"type":"string","enum":["admin","operator","viewer","demo"],"title":"UserRole"},"UserUpdate":{"properties":{"role":{"anyOf":[{"$ref":"#/components/schemas/UserRole"},{"type":"null"}],"description":"Change what the account may do. The last administrator cannot be demoted."},"password":{"anyOf":[{"type":"string","maxLength":256,"minLength":8},{"type":"null"}],"title":"Password","description":"Set a new password, signing every session for that account out."}},"additionalProperties":false,"type":"object","title":"UserUpdate"},"WatchCreate":{"properties":{"platform":{"$ref":"#/components/schemas/Platform","description":"Which platform the target is on."},"kind":{"type":"string","pattern":"^(author|content)$","title":"Kind","description":"`author` to watch someone's posts for new ones, `content` to watch one post's counters."},"target_id":{"type":"string","maxLength":128,"minLength":1,"title":"Target Id","description":"`sec_user_id` or `aweme_id`, as the platform spells it."},"label":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Label","description":"What to call it in the console. Filled in from the first successful run when omitted, so pasting a bare id is fine."},"interval_seconds":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Interval Seconds","description":"How often to run it. Omitted means `watchlist.default_interval_seconds`. Anything under `watchlist.min_interval_seconds` is refused rather than silently raised to a number you did not ask for."},"pages":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Pages","description":"Pages of an author's posts one run walks. One is the useful default: a watchlist is for what is new, and a backfill is a different job.","default":1}},"additionalProperties":false,"type":"object","required":["platform","kind","target_id"],"title":"WatchCreate","description":"Add one target to the schedule.\n\nA content key, the same as a download: there is no URL here either. What\nthis creates is a standing instruction to spend the identity pool every few\nhours, which is why it is administrative rather than a data endpoint."},"WatchKind":{"type":"string","enum":["author","content"],"title":"WatchKind","description":"What a watchlist entry follows."},"WatchUpdate":{"properties":{"interval_seconds":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Interval Seconds","description":"Change how often it runs. Same floor as on create."},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled","description":"Pause it or start it again. A paused entry keeps its history."},"pages":{"anyOf":[{"type":"integer","maximum":10.0,"minimum":1.0},{"type":"null"}],"title":"Pages","description":"Change how many pages one run walks."}},"additionalProperties":false,"type":"object","title":"WatchUpdate"},"DtkError":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable machine-readable identifier. Never translated.","example":"IDENTITY_POOL_EXHAUSTED"},"message":{"type":"string","description":"Human sentence, rendered in the caller's language."},"details":{"type":"object","additionalProperties":true,"description":"What the error was about, keyed by name - the field that failed, the endpoint that refused, the id that was not found. Shape varies by code; never contains a credential."},"retry_after":{"type":"integer","nullable":true,"description":"Seconds to wait, when the error is one that clears."},"retryable":{"type":"boolean","description":"Whether sending the same request again could succeed. False for anything about the request itself, so an agent reading this does not retry a URL that will never work."}}},"DtkResponse":{"type":"object","required":["success","data","error","meta"],"description":"Every response has this shape, errors included. `data` carries the endpoint's own payload and is null whenever `success` is false; `error.code` is a stable identifier that is never translated, while `error.message` is rendered in the language you asked for.","properties":{"success":{"type":"boolean","description":"False whenever `error` is set, and never otherwise."},"data":{"nullable":true,"description":"The endpoint's payload."},"error":{"oneOf":[{"$ref":"#/components/schemas/DtkError"}],"nullable":true,"description":"What went wrong, or null. Present on every failure."},"meta":{"type":"object","additionalProperties":true,"description":"About the call rather than its result: the request id to quote in a bug report, whether the answer was cached, how long it took, and the paging cursor when there is one.","properties":{"request_id":{"type":"string","format":"uuid","description":"Correlates this response with the request log."}}}}}},"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Create one on the console's API keys page."},"SessionCookie":{"type":"apiKey","in":"cookie","name":"dtk_session","description":"Set by signing in to the console; sent automatically by a browser."}}},"tags":[{"name":"admin","description":"Instance administration; requires the admin scope."},{"name":"archive","description":"What this instance has already collected. Answered from local storage."},{"name":"auth","description":"Signing in, sessions, and the account you are signed in as."},{"name":"content","description":"Platform-agnostic reads: hand any of these a link or an id."},{"name":"downloads","description":"Media this instance has stored on its own disk. A sink, never a relay: bytes are never streamed back to a caller."},{"name":"ios","description":"The iOS Shortcut this instance serves, ready to install."},{"name":"setup","description":"First-run: the only endpoints that answer before an administrator exists."},{"name":"system","description":"Health, version and diagnostics."},{"name":"tasks","description":"Asynchronous task submission, polling and events."},{"name":"tools","description":"Building blocks: link parsing, signing and guest identity minting."}]}