{"schema":"missiond.service-layer-aibot-channel.v1","name":"readvideo","summary":"Turn a video or audio link into a timestamped, readable transcript with chapters.","base_url":"https://asr.xiaojins.com","console_url":"https://readvideo.ai/api-access","auth":{"channels":["apikey","aibot"],"apikey":{"how":"Send the opaque key as an HTTP Bearer token over TLS.","header":"Authorization: Bearer <READVIDEO_API_KEY>","obtain":"https://readvideo.ai/api-access","secret_manager_env_name":"READVIDEO_API_KEY","never":"Do not place the key in a query string, a repository, or a chat message."},"aibot":{"how":"Exchange the agent's long-lived api_key for a short-lived JWT, then send that JWT as the Bearer token.","token_endpoint":"https://auth.xiaojins.com/oauth2/token","grant_type":"urn:xjp:bot_api_key","required_fields":["grant_type","client_id","api_key"],"client_id":"readvideo","capability_required":"asr:user","capability_note":"The agent must hold the asr:user capability (agents created on the console get it; older ones enable it there). It scopes the agent to its owner's own data, billed to the owner.","secret_manager_env_names":["READVIDEO_AGENT_API_KEY"],"never":"The agent api_key stays in the agent runtime's secret manager. It is never sent to this API directly, embedded in a skill, or shown to a user.","token_lifetime_note":"Access tokens are short-lived. Mint one per run and refresh on 401 rather than caching across runs."}},"actions":[{"id":"submit_link","method":"POST","path":"/v1/transcribe/link","scope":"asr:write","body":{"url":"string (public video or audio URL)"},"returns":"task_id, status","notes":"Asynchronous. Resubmitting the same URL returns the cached task instead of charging again."},{"id":"get_task","method":"GET","path":"/v1/tasks/{task_id}","scope":"asr:read","returns":"status (pending|processing|completed|failed), transcript_data when completed, chapters, audio_duration_seconds"},{"id":"list_transcriptions","method":"GET","path":"/v1/users/me/transcriptions?limit={limit}&offset={offset}","scope":"asr:read","returns":"paged task summaries owned by the credential's user"},{"id":"get_media","method":"GET","path":"/v1/tasks/{task_id}/media","scope":"asr:read","returns":"a short-lived signed playback URL, or media_status=expired when one cannot be issued"},{"id":"create_upload","method":"POST","path":"/v1/uploads","scope":"asr:write","body":{"content_type":"string (e.g. video/mp4, audio/m4a)","size_bytes":"number","file_sha256":"string (hex sha256 of the file bytes)","media_kind":"audio | video","duration_seconds":"number | null"},"returns":"upload.url (presigned PUT target) + object_key + source_url; upload_required=false when the same bytes were uploaded before","notes":"For LOCAL audio/video files: PUT the raw file to upload.url with the same Content-Type header, then call import_upload. Same bytes are deduplicated by sha256."},{"id":"import_upload","method":"POST","path":"/v1/transcriptions/import","scope":"asr:write","body":{"url":"source_url from create_upload","object_key":"object_key from create_upload","content_type":"same as create_upload","size_bytes":"number","duration_seconds":"number","media_kind":"audio | video","options":"optional transcription options (language_hint, diarization)"},"returns":"task_id — poll get_task like any other submission"},{"id":"submit_upload_multipart","method":"POST","path":"/v1/transcribe/upload","scope":"asr:write","body":"multipart/form-data: file=<binary>, options=<json string>","returns":"task_id","notes":"Single-request alternative for local files up to 200MB. Larger files must use create_upload + import_upload."},{"id":"get_credits","method":"GET","path":"/v1/billing/me","scope":"asr:read","returns":"credit balance and plan","notes":"Check this before submitting long media; a valid credential is not credit."}],"input_limits":{"accepted_sources":"Public video/audio links, and direct uploads via /v1/uploads.","max_upload_mb":512,"note":"Very long media is split internally; a single submission may take many minutes."},"polling":{"style":"poll","endpoint":"/v1/tasks/{task_id}","recommended_interval_seconds":10,"terminal_states":["completed","failed"],"note":"There is no webhook. Do not poll faster than the rate limit on your credential."},"retry_policy":{"safe_to_retry":["GET requests","resubmitting the same link URL"],"backoff":"Exponential from 2s, capped at 60s.","on_401":"Mint a fresh token (AIBOT) or stop and report a revoked key (APIKEY). Never retry a 401 in a tight loop.","on_402":"Insufficient credits — stop and tell the human. Retrying cannot succeed.","on_429":"Respect the limit on the credential; slow down rather than switching keys."},"error_codes":{"401":"Missing, expired, or revoked credential.","402":"Insufficient credits for this job.","403":"Credential lacks the required scope, or the object belongs to another user.","404":"Unknown task id, or a task the credential cannot see.","413":"Upload exceeds the size limit.","429":"Rate limit exceeded for this credential.","5xx":"Transient upstream failure — retry with backoff."},"billing_and_permission_warning":"Both channels spend the credits of the human account that issued the credential. An agent acting on delegated identity does not bypass that account's payment entitlement, legal acceptance, or privacy settings.","destructive_actions":{"policy":"Deleting a task or revoking a credential is irreversible and requires explicit human confirmation in the same turn. An agent must never revoke or delete on its own initiative.","endpoints":["DELETE /v1/tasks/{task_id}"]},"privacy_and_retention":"Submitted media and generated transcripts are stored under the issuing user's account and follow the retention stated at https://readvideo.ai/privacy. Do not submit content the user is not entitled to process.","support":"support@readvideo.ai — quote the task id or key id, never the credential."}