From db95c2b44e415ae963faaa7c0ca491653cdf3f96 Mon Sep 17 00:00:00 2001 From: KKKK Date: Tue, 8 Sep 2026 20:44:01 +0800 Subject: [PATCH 1/3] Update client for current BeatAPI capabilities --- contract/beatapi.openapi.yaml | 2537 +++++++++++++++++-- contract/contract.lock.json | 4 +- package-lock.json | 14 +- packages/client/src/client.ts | 99 +- packages/client/src/index.ts | 9 + packages/client/src/types.generated.ts | 3220 +++++++++++++++++++++--- packages/client/test/client.test.ts | 186 ++ 7 files changed, 5509 insertions(+), 560 deletions(-) diff --git a/contract/beatapi.openapi.yaml b/contract/beatapi.openapi.yaml index 65e1dc9..a988a34 100644 --- a/contract/beatapi.openapi.yaml +++ b/contract/beatapi.openapi.yaml @@ -6,11 +6,15 @@ info: name: BeatAPI Terms of Service url: https://beatapi.io/terms-of-service description: | - BeatAPI provides async video workflows and short-lived Realtime Video - Sessions behind one BeatAPI-native API. Async integrations create a task, - poll until it finishes, then read the hosted video URL from `output.media`. - Realtime browser integrations create a Session with the same Bearer API key, - then pass only the returned BeatAPI `client_secret` to `@beatapi/realtime`. + BeatAPI provides one API key for public text models, image generation, video generation, + video analysis, Effects, asynchronous video workflows, and short-lived Realtime Video Sessions. + + For asynchronous operations, create a task, poll the shared task endpoint or receive webhook events, + and read hosted output URLs from `output.media`. For Realtime, create a Session on a trusted server + with your Bearer API key and pass only the returned short-lived `client_secret` to the browser. + + Customer balances and usage are USD-denominated. Compatibility fields such + as `credit_balance` and `credits_reserved` remain in the API; 1 Credit = $1 USD. ## 5 minute Quick Start @@ -18,7 +22,7 @@ info: 2. Create an API key in [Dashboard → API Keys](https://beatapi.io/dashboard/apikeys) and send it as `Authorization: Bearer `. - Credit packs are available from + USD balance top-ups are available from [Dashboard → Billing](https://beatapi.io/dashboard/billing). 3. Use public HTTPS URLs for input media. If your files are local, upload them with `POST /v1/files` first. @@ -27,7 +31,8 @@ info: 6. Add webhooks later if you do not want to poll. ```bash - export BEATAPI_API_KEY="sk_your_key" + read -rsp "BeatAPI API key: " BEATAPI_API_KEY && echo + export BEATAPI_API_KEY curl https://api.beatapi.io/v1/workflows @@ -69,15 +74,15 @@ info: Public workflow inputs must use HTTPS URLs that are reachable from the public internet. Localhost, private network URLs, and data URLs are rejected. - Use `POST /v1/files` for local images, audio, or subtitles. + Use `POST /v1/files` for local images, audio, subtitles, or Motion Control reference videos. - Each verified new user account starts with 50 welcome credits valid for 14 days and - 1 active processing concurrency. Lifetime paid credit purchases unlock higher limits: + Each verified new user account starts with a $2 welcome balance that never expires and + 1 active processing concurrency. Lifetime paid purchases unlock higher limits: $10+ = 2, $100+ = 5, $1,000+ = 10, $5,000+ = 15, and $20,000+ = 30. `GET /v1/usage` returns current usage totals, concurrency limit, and active processing task count. Active concurrency measures tasks that are currently using BeatAPI processing resources. storyboard_ready and requires_action - tasks can have settled credits but do not count toward active processing + tasks can have settled USD usage but do not count toward active processing concurrency. ## Webhooks are optional @@ -95,8 +100,16 @@ tags: description: Discover the workflow IDs available for task creation. - name: Music Video description: Create music video tasks from images, audio, and optional creative controls. + - name: Effects + description: Discover versioned effects and create image or video effect tasks. + - name: Generation + description: Discover BeatAPI generation models and create image or video tasks. + - name: Text + description: Discover enabled text models and call them through OpenAI, Anthropic, or Gemini-compatible request formats. - name: Ecommerce Video description: Create product ad video tasks from product images and duration. + - name: Video Analysis + description: Analyze uploaded videos with standard or deep multimodal reasoning. - name: Tasks description: Poll task status and read output URLs. - name: Usage @@ -107,6 +120,70 @@ tags: description: Upload local assets and use the returned HTTPS URL as workflow input. - name: Webhooks description: Manage optional completion callbacks. +webhooks: + taskCompleted: + post: + operationId: receiveBeatApiTaskEvent + x-fern-ignore: true + tags: [Webhooks] + summary: Receive a BeatAPI task completion event + description: | + BeatAPI sends this request to each active endpoint subscribed to the event. + Verify `x-beatapi-signature` against the exact request body and use polling + as the source of truth if delivery is delayed or fails. + security: [] + parameters: + - in: header + name: x-beatapi-event + required: true + schema: { type: string, enum: [task.succeeded, task.failed] } + - in: header + name: x-beatapi-timestamp + required: true + schema: { type: string } + - in: header + name: x-beatapi-signature + required: true + schema: { type: string } + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/WebhookEvent' } + example: + id: evt_123 + event: task.succeeded + created_at: 1782210300 + data: + id: task_8K2qA + object: task + task_kind: video + capability_id: seedance-2.5 + capability_version: null + media_type: video + model: seedance-2.5 + status: succeeded + stage: succeeded + created_at: 1782210000 + updated_at: 1782210300 + completed_at: 1782210300 + output: + media: + - type: video + url: https://media.beatapi.io/outputs/task_8K2qA/0.mp4 + mime_type: video/mp4 + r2_url: https://media.beatapi.io/outputs/task_8K2qA/0.mp4 + usage: + credits_reserved: 1.55 + credits_charged: 1.55 + billable_duration_seconds: 5 + credits_settled: 1.55 + credits_refunded: 0 + request_id: req_abc123 + error_code: null + error_message: null + responses: + '200': { description: Event accepted } components: securitySchemes: BearerAuth: @@ -114,14 +191,61 @@ components: scheme: bearer bearerFormat: sk_xxx description: 'Send your API key as `Authorization: Bearer `.' + ApiKeyHeader: + type: apiKey + in: header + name: x-api-key + description: Anthropic-compatible API key header. Use a BeatAPI API key. + GoogleApiKeyHeader: + type: apiKey + in: header + name: x-goog-api-key + description: Gemini-compatible API key header. Use a BeatAPI API key. + GoogleApiKeyQuery: + type: apiKey + in: query + name: key + description: Gemini SDK compatibility only. Prefer the x-goog-api-key header when possible. schemas: + TextModelId: + type: string + description: Public text model id exposed by BeatAPI. Call GET /v1/models to discover the models enabled for your environment. + TextModel: + type: object + additionalProperties: false + required: [id, object, created, owned_by] + properties: + id: { $ref: '#/components/schemas/TextModelId' } + object: { type: string, const: model } + created: { type: integer, example: 1788220800 } + owned_by: { type: string, const: beatapi } + TextModelList: + type: object + additionalProperties: false + required: [object, data] + properties: + object: { type: string, const: list } + data: + type: array + items: { $ref: '#/components/schemas/TextModel' } + TextPassthroughRequest: + type: object + description: SDK-compatible text request. BeatAPI preserves supported provider-format fields and streams the matching response format back. + required: [model] + properties: + model: { $ref: '#/components/schemas/TextModelId' } + additionalProperties: true + TextPassthroughResponse: + type: object + description: Response body in the selected SDK-compatible wire format. + additionalProperties: true Workflow: type: object required: [id, object, name, description] properties: id: type: string - enum: [music-video, ecommerce-video] + enum: [music-video, ecommerce-video, video-analysis] example: music-video object: type: string @@ -144,17 +268,22 @@ components: example: shot_xxx index: type: integer + description: Zero-based shot order in the storyboard. example: 0 status: $ref: '#/components/schemas/TaskStatus' + description: Current lifecycle state for this storyboard shot. duration_seconds: type: integer + description: Planned or generated shot duration in seconds. example: 5 prompt: type: string + description: Creative instruction used to generate this shot. example: Opening lyric shot. lyric_text: type: string + description: Lyric segment aligned with this shot when available. example: Intro media: type: object @@ -162,19 +291,24 @@ components: properties: type: type: string + description: Hosted media type for the materialized shot. example: video url: type: string format: uri + description: BeatAPI-hosted HTTPS URL for the materialized shot. example: https://media.beatapi.io/outputs/task_8K2qA/shots/0.mp4 mime_type: type: string + description: MIME type of the hosted shot media. example: video/mp4 created_at: type: integer + description: Unix timestamp when the shot record was created. example: 1782210000 updated_at: type: integer + description: Unix timestamp when the shot record last changed. example: 1782210300 Storyboard: type: object @@ -182,6 +316,7 @@ components: properties: shots: type: array + description: Ordered Music Video storyboard shots. The array may be empty before storyboard generation completes. items: $ref: '#/components/schemas/StoryboardShot' ShotMedia: @@ -230,46 +365,88 @@ components: required: [credits_reserved, credits_settled, credits_refunded, credits_charged] properties: credits_reserved: - type: integer - description: BeatAPI customer credits reserved for this task. + type: number + format: double + multipleOf: 0.01 + description: USD amount reserved for this task. The compatibility field name is retained; 1 Credit equals $1 USD. credits_charged: - type: integer - description: BeatAPI customer credits charged when the task or operation is accepted. + type: number + format: double + multipleOf: 0.01 + description: USD amount charged when the task or operation is accepted. billable_duration_seconds: type: integer - description: Server-detected or request-declared billable duration used for credit calculation. + description: Server-detected or request-declared billable duration used for USD calculation. credits_settled: - type: integer - description: BeatAPI customer credits settled after successful work. + type: number + format: double + multipleOf: 0.01 + description: USD amount settled after successful work. credits_refunded: - type: integer - description: BeatAPI customer credits refunded after failed eligible work. + type: number + format: double + multipleOf: 0.01 + description: USD amount refunded after failed eligible work. Task: type: object - required: [id, object, workflow, status, stage, created_at, updated_at, completed_at, output, usage, request_id, error_code, error_message] + required: [id, object, task_kind, capability_id, capability_version, status, stage, created_at, updated_at, completed_at, output, usage, request_id, error_code, error_message] properties: id: type: string + description: Stable BeatAPI task ID used for polling and support. example: task_8K2qA object: type: string enum: [task] + description: Object discriminator; always `task`. + task_kind: + type: string + enum: [workflow, effect, image, video] + description: Public task family that determines which capability fields are present. + capability_id: + type: string + description: Stable BeatAPI workflow, Effect, or generation model ID selected when the task was accepted. + capability_version: + type: [integer, 'null'] + description: Immutable capability version used by this task. Legacy workflow rows are returned as version 1. workflow: type: string - enum: [music-video, ecommerce-video] + enum: [music-video, ecommerce-video, video-analysis] + description: Present for workflow tasks; identifies the selected BeatAPI workflow. example: music-video + effect_id: + type: string + description: Present for Effect tasks; stable selected Effect ID. + example: video-muscle-max + effect_version: + type: integer + description: Present for Effect tasks; immutable Effect version used for processing. + example: 1 + media_type: + type: string + enum: [image, video] + description: Present when task_kind is image or video. + model: + type: string + description: Stable BeatAPI model alias. It is independent from internal execution routing. status: $ref: '#/components/schemas/TaskStatus' + description: Current task lifecycle status. Stop polling at `succeeded` or `failed`; Music Video can also require manual action. stage: $ref: '#/components/schemas/TaskStatus' + description: Current processing stage, exposed separately so workflow progress can be tracked. storyboard: $ref: '#/components/schemas/Storyboard' + description: Music Video storyboard metadata when available. created_at: type: integer + description: Unix timestamp when BeatAPI accepted the task. updated_at: type: integer + description: Unix timestamp of the latest task update. completed_at: type: [integer, 'null'] + description: Terminal Unix timestamp, or null while work is in progress. output: description: Output is null until the task succeeds. oneOf: @@ -279,54 +456,155 @@ components: properties: media: type: array + description: BeatAPI-hosted result assets. items: type: object required: [type, url, mime_type] properties: type: type: string - enum: [video] + enum: [image, video] + description: Result asset type. url: type: string format: uri + description: BeatAPI-hosted HTTPS result URL. mime_type: type: string - example: video/mp4 + description: Result asset MIME type. + examples: [video/mp4, image/png, image/jpeg, image/webp] r2_url: type: string format: uri + description: Primary BeatAPI-hosted result URL for clients that need one canonical asset. + - type: object + required: [text, usage, finish_reason] + properties: + text: + type: string + description: Completed video analysis text. + usage: + type: object + description: Measured token usage used for final USD settlement. + required: [input_tokens, output_tokens, total_tokens] + properties: + input_tokens: + type: integer + minimum: 0 + description: Tokens consumed by the prompt and video input. + output_tokens: + type: integer + minimum: 0 + description: Tokens consumed by visible output and model reasoning. + total_tokens: + type: integer + minimum: 0 + description: Total measured input and output tokens. + finish_reason: + type: [string, 'null'] + description: Upstream-compatible completion reason. usage: $ref: '#/components/schemas/TaskUsage' + description: USD reservation, settlement, refund, and optional billable duration for this task. request_id: type: string + description: Correlation ID to retain for logs and BeatAPI support. example: req_abc123 error_code: type: [string, 'null'] + description: Machine-readable terminal failure code, or null when no task failure is recorded. example: processing_timeout error_message: type: [string, 'null'] + description: Human-readable terminal failure detail, or null when no task failure is recorded. + Effect: + type: object + required: [id, object, name, description, output_type, category, tags, input, options, preview, version, status] + properties: + id: { type: string, example: video-muscle-max } + object: { type: string, enum: [effect] } + name: { type: string, example: Muscle Transformation } + description: { type: string } + output_type: { type: string, enum: [image, video] } + category: { type: string, example: transformation } + tags: { type: array, items: { type: string } } + input: + type: object + required: [images_min, images_max, accepted_types] + properties: + images_min: { type: integer, minimum: 1 } + images_max: { type: integer, minimum: 1 } + accepted_types: + type: array + items: { type: string, enum: [image/jpeg, image/png, image/webp] } + max_size_mb: + type: integer + minimum: 1 + description: Maximum downloaded bytes per input image. When omitted, BeatAPI enforces 50 MB. + max_dimension_px: + type: integer + minimum: 1 + description: Maximum decoded width or height. BeatAPI inspects the actual image header before charging. + subject_requirements: { type: array, items: { type: string } } + options: + type: object + properties: + aspect_ratios: { type: array, items: { type: string } } + resolutions: { type: array, items: { type: string } } + duration_seconds: { type: array, items: { type: integer } } + bgm: { type: boolean } + seed: { type: boolean } + preview: + type: object + required: [cover_url, media_url] + properties: + cover_url: { type: [string, 'null'], format: uri } + media_url: { type: [string, 'null'], format: uri } + version: { type: integer, minimum: 1 } + status: { type: string, enum: [testing, active, paused] } + EffectResponse: + type: object + required: [data] + properties: + data: { $ref: '#/components/schemas/Effect' } + EffectListResponse: + type: object + required: [data] + properties: + data: + type: object + required: [object, data] + properties: + object: { type: string, enum: [list] } + data: { type: array, items: { $ref: '#/components/schemas/Effect' } } File: type: object required: [id, object, url, key, mime_type, size_bytes, purpose, created_at] properties: id: type: string + description: Stable uploaded file ID. example: file_3xYz9 object: type: string enum: [file] + description: Object discriminator; always `file`. url: type: string format: uri + description: Long-lived BeatAPI HTTPS URL to use in workflow or model requests. example: https://media.beatapi.io/inputs/file_3xYz9.mp3 key: type: string + description: BeatAPI storage key for support and diagnostics. example: inputs/file_3xYz9.mp3 mime_type: type: string + description: Accepted MIME type detected for the uploaded file. example: audio/mpeg size_bytes: type: integer + description: Uploaded file size in bytes. example: 1048576 audio_duration_seconds: type: number @@ -336,11 +614,29 @@ components: type: string description: Duration detection method used for uploaded audio. example: mp3_frame_scan + video_duration_seconds: + type: number + description: Present for MP4/MOV uploads after server-side container inspection. + example: 15.25 + video_duration_source: + type: string + description: Duration and dimension detection method used for the uploaded video. + example: mp4_boxes + width: + type: integer + description: Detected pixel width for uploaded images and videos. + example: 720 + height: + type: integer + description: Detected pixel height for uploaded images and videos. + example: 1280 purpose: type: string enum: [input] + description: File purpose; currently always `input`. created_at: type: integer + description: Unix timestamp when the file was stored. example: 1782210000 WebhookEndpoint: type: object @@ -348,33 +644,41 @@ components: properties: id: type: string + description: Stable webhook endpoint ID used for get, update, and delete operations. example: wh_9aBcD object: type: string enum: [webhook_endpoint] + description: Object discriminator; always `webhook_endpoint`. url: type: string format: uri + description: Public HTTPS callback URL receiving subscribed task events. example: https://example.com/beatapi-webhook description: type: string + description: Account-defined label for the endpoint. example: Production webhook events: type: array + description: Task event types delivered to this endpoint. items: type: string enum: [task.succeeded, task.failed] status: type: string enum: [active, disabled] + description: Delivery status. Disabled endpoints do not receive events. secret: type: string description: Returned in full only when the endpoint is created. Later responses return a masked value. example: whsec_example_masked created_at: type: integer + description: Unix timestamp when the endpoint was created. updated_at: type: integer + description: Unix timestamp when the endpoint last changed. WebhookEvent: type: object required: [id, event, created_at, data] @@ -406,28 +710,576 @@ components: properties: data: $ref: '#/components/schemas/WorkflowList' + GenerationModel: + type: object + additionalProperties: false + required: [id, object, name, media_type, input_modes] + properties: + id: + type: string + enum: [nano-banana, nano-banana-2, nano-banana-2-lite, nano-banana-pro, gpt-image-2, seedream-5-pro, grok-imagine-image-2.0, minimax-h3, grok-imagine-video-1.5, seedance-2, seedance-2-fast, seedance-2-mini, veo-3.1, seedance-2.5, kling-3, kling-2.6-motion-control, kling-3-motion-control, wan-3.0, wan-3.0-prime, happyhorse-1.0, happyhorse-1.1, minimax-h3-max, minimax-h3-max-turbo] + object: { type: string, enum: [generation_model] } + name: { type: string } + media_type: { type: string, enum: [image, video] } + input_modes: + type: array + items: { type: string, enum: [text, image, frames, reference] } + GenerationModelList: + type: object + required: [object, data] + properties: + object: { type: string, enum: [list] } + data: + type: array + items: { $ref: '#/components/schemas/GenerationModel' } + GenerationModelListResponse: + type: object + required: [data] + properties: + data: { $ref: '#/components/schemas/GenerationModelList' } + ImageGenerationTaskCreateRequest: + oneOf: + - $ref: '#/components/schemas/NanoBananaImageRequest' + - $ref: '#/components/schemas/NanoBanana2ImageRequest' + - $ref: '#/components/schemas/NanoBanana2LiteImageRequest' + - $ref: '#/components/schemas/NanoBananaProImageRequest' + - $ref: '#/components/schemas/GptImage2Request' + - $ref: '#/components/schemas/Seedream5ProImageRequest' + - $ref: '#/components/schemas/GrokImagineImage20Request' + discriminator: + propertyName: model + mapping: + nano-banana: '#/components/schemas/NanoBananaImageRequest' + nano-banana-2: '#/components/schemas/NanoBanana2ImageRequest' + nano-banana-2-lite: '#/components/schemas/NanoBanana2LiteImageRequest' + nano-banana-pro: '#/components/schemas/NanoBananaProImageRequest' + gpt-image-2: '#/components/schemas/GptImage2Request' + seedream-5-pro: '#/components/schemas/Seedream5ProImageRequest' + grok-imagine-image-2.0: '#/components/schemas/GrokImagineImage20Request' + NanoBananaImageRequest: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: nano-banana, description: Must be `nano-banana`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 10 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: ['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', auto] + default: '1:1' + description: Output image aspect ratio. + output_format: { type: string, enum: [png, jpeg], default: png, description: Output image file format. } + NanoBanana2ImageRequest: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: nano-banana-2, description: Must be `nano-banana-2`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 10 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: ['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', auto] + default: '1:1' + description: Output image aspect ratio. + resolution: { type: string, enum: [1K, 2K, 4K], default: 1K, description: Output resolution tier. } + output_format: { type: string, enum: [png, jpeg], default: png, description: Output image file format. } + NanoBanana2LiteImageRequest: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: nano-banana-2-lite, description: Must be `nano-banana-2-lite`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 10 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: ['1:1', '9:16', '16:9', '3:4', '4:3', '3:2', '2:3', '5:4', '4:5', '21:9', auto] + default: '1:1' + description: Output image aspect ratio. + output_format: { type: string, enum: [png, jpeg], default: png, description: Output image file format. } + NanoBananaProImageRequest: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: nano-banana-pro, description: Must be `nano-banana-pro`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 8 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: ['1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', auto] + default: '1:1' + description: Output image aspect ratio. + resolution: { type: string, enum: [1K, 2K, 4K], default: 1K, description: Output resolution tier. } + output_format: { type: string, enum: [png, jpg], default: png, description: Output image file format. } + GptImage2Request: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: gpt-image-2, description: Must be `gpt-image-2`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 16 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: [auto, '1:1', '3:2', '2:3', '4:3', '3:4', '5:4', '4:5', '16:9', '9:16', '2:1', '1:2', '3:1', '1:3', '21:9', '9:21'] + default: auto + description: Output image aspect ratio. + resolution: { type: string, enum: [1K, 2K, 4K], default: 1K, description: Output resolution tier. } + Seedream5ProImageRequest: + type: object + additionalProperties: false + required: [model, prompt] + properties: + model: { type: string, const: seedream-5-pro, description: Must be `seedream-5-pro`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 10 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: [auto, '1:1', '4:3', '3:4', '16:9', '9:16', '3:2', '2:3', '21:9'] + default: '1:1' + description: Output image aspect ratio. + resolution: { type: string, enum: [1K, 2K, 4K], default: 1K, description: Output resolution tier. } + output_format: { type: string, enum: [png, jpeg], default: png, description: Output image file format. } + GrokImagineImage20Request: + type: object + additionalProperties: false + required: [model, prompt] + description: Omit `images` for text-to-image. Supply one to five images for editing; `auto` aspect ratio is available only when images are supplied. + properties: + model: { type: string, const: grok-imagine-image-2.0, description: Must be `grok-imagine-image-2.0`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Generation or image-editing instructions. } + images: + type: array + minItems: 1 + maxItems: 5 + description: Public HTTPS reference-image URLs. Omit for text-to-image. + items: { type: string, format: uri, pattern: '^https://' } + aspect_ratio: + type: string + enum: ['1:1', '2:3', '3:2', '16:9', '9:16', auto] + default: '1:1' + description: Output image aspect ratio. `auto` requires at least one image. + VideoGenerationTaskCreateRequest: + oneOf: + - $ref: '#/components/schemas/MinimaxH3VideoRequest' + - $ref: '#/components/schemas/GrokImagineVideo15Request' + - $ref: '#/components/schemas/Seedance2VideoRequest' + - $ref: '#/components/schemas/Seedance2FastVideoRequest' + - $ref: '#/components/schemas/Seedance2MiniVideoRequest' + - $ref: '#/components/schemas/Veo31VideoRequest' + - $ref: '#/components/schemas/Seedance25VideoRequest' + - $ref: '#/components/schemas/Kling3VideoRequest' + - $ref: '#/components/schemas/Kling26MotionControlVideoRequest' + - $ref: '#/components/schemas/Kling3MotionControlVideoRequest' + - $ref: '#/components/schemas/Wan30VideoRequest' + - $ref: '#/components/schemas/Wan30PrimeVideoRequest' + - $ref: '#/components/schemas/HappyHorse10VideoRequest' + - $ref: '#/components/schemas/HappyHorse11VideoRequest' + - $ref: '#/components/schemas/MinimaxH3MaxVideoRequest' + - $ref: '#/components/schemas/MinimaxH3MaxTurboVideoRequest' + discriminator: + propertyName: model + mapping: + minimax-h3: '#/components/schemas/MinimaxH3VideoRequest' + grok-imagine-video-1.5: '#/components/schemas/GrokImagineVideo15Request' + seedance-2: '#/components/schemas/Seedance2VideoRequest' + seedance-2-fast: '#/components/schemas/Seedance2FastVideoRequest' + seedance-2-mini: '#/components/schemas/Seedance2MiniVideoRequest' + veo-3.1: '#/components/schemas/Veo31VideoRequest' + seedance-2.5: '#/components/schemas/Seedance25VideoRequest' + kling-3: '#/components/schemas/Kling3VideoRequest' + kling-2.6-motion-control: '#/components/schemas/Kling26MotionControlVideoRequest' + kling-3-motion-control: '#/components/schemas/Kling3MotionControlVideoRequest' + wan-3.0: '#/components/schemas/Wan30VideoRequest' + wan-3.0-prime: '#/components/schemas/Wan30PrimeVideoRequest' + happyhorse-1.0: '#/components/schemas/HappyHorse10VideoRequest' + happyhorse-1.1: '#/components/schemas/HappyHorse11VideoRequest' + minimax-h3-max: '#/components/schemas/MinimaxH3MaxVideoRequest' + minimax-h3-max-turbo: '#/components/schemas/MinimaxH3MaxTurboVideoRequest' + MinimaxH3VideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: '`images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video.' + properties: + model: { type: string, const: minimax-h3, description: Must be `minimax-h3`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 9, description: Public HTTPS image references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS video references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS audio references for multimodal reference generation. Audio also requires at least one reference image or video., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 4, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: + type: string + enum: [adaptive, '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'] + description: Text mode defaults to 16:9 and does not accept adaptive. Frame mode always uses adaptive. Reference mode defaults to adaptive and also accepts a concrete ratio. + resolution: { type: string, enum: [768P, 2K], default: 768P, description: Output resolution tier. } + GrokImagineVideo15Request: + type: object + additionalProperties: false + required: [model, prompt] + description: '`images` accepts one first frame and cannot be combined with `reference_images`. Omit `aspect_ratio` when `images` is supplied. 1080p accepts at most one image.' + properties: + model: { type: string, const: grok-imagine-video-1.5, description: Must be `grok-imagine-video-1.5`. } + prompt: { type: string, minLength: 1, maxLength: 4096, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 1, description: One first-frame image as a public HTTPS URL., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 7, description: One to seven public HTTPS reference images., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 1, maximum: 15, default: 8, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: ['1:1', '16:9', '9:16', '3:2', '2:3', auto], default: '16:9', description: Output video aspect ratio. Omit when one first-frame image is supplied. } + resolution: { type: string, enum: [480p, 720p, 1080p], default: 480p, description: Output resolution tier. 1080p accepts at most one image. } + Seedance2VideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: '`images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video.' + properties: + model: { type: string, const: seedance-2, description: Must be `seedance-2`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 9, description: Public HTTPS image references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS video references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS audio references. Audio also requires at least one reference image or video., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 4, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: [adaptive, '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], default: adaptive, description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p, 1080p, 4k, 4K], default: 720p, description: Output resolution tier. 4k and 4K are equivalent. 1080p is not supported with reference images. } + generate_audio: { type: boolean, default: true, description: Generate synchronized audio with the video. } + Seedance2FastVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: '`images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video.' + properties: + model: { type: string, const: seedance-2-fast, description: Must be `seedance-2-fast`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 9, description: Public HTTPS image references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS video references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS audio references. Audio also requires at least one reference image or video., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 4, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: [adaptive, '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], default: adaptive, description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p], default: 720p, description: Output resolution tier. } + generate_audio: { type: boolean, default: true, description: Generate synchronized audio with the video. } + Seedance2MiniVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: 'Low-cost Seedance 2.0 route. `images` cannot be combined with any `reference_*` input. Generated audio is not supported.' + properties: + model: { type: string, const: seedance-2-mini, description: Must be `seedance-2-mini`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 9, description: Public HTTPS image references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS video references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS audio references. Audio also requires at least one reference image or video., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 4, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: [adaptive, '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], default: adaptive, description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p], default: 720p, description: Output resolution tier. } + Veo31VideoRequest: + allOf: + - oneOf: + - $ref: '#/components/schemas/Veo31TextOrFrameVideoRequest' + - $ref: '#/components/schemas/Veo31ReferenceVideoRequest' + Veo31TextOrFrameVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: | + Veo 3.1 text or first/last-frame generation. Output is fixed at 8 seconds + and defaults to Quality at 720p. Price depends on quality and resolution. + properties: + model: { type: string, const: veo-3.1, description: Must be `veo-3.1`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + aspect_ratio: { type: string, enum: ['16:9', '9:16', auto], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [720p, 1080p, 4k, 4K], default: 720p, description: Output video resolution. 4k and 4K are equivalent. Price depends on quality and resolution. } + quality: { type: string, enum: [Quality, Fast, Lite], default: Quality, description: Text or frame generation tier. } + watermark: { type: string, description: Optional watermark text forwarded to the selected model. } + enable_translation: { type: boolean, description: Allow prompt translation before generation. } + Veo31ReferenceVideoRequest: + type: object + additionalProperties: false + required: [model, prompt, reference_images] + description: | + Veo 3.1 reference-image generation. Output is fixed at 8 seconds and + supports the Fast or Lite tier, defaulting to Fast at 720p. Price depends + on quality and resolution. + properties: + model: { type: string, const: veo-3.1, description: Must be `veo-3.1`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + reference_images: { type: array, minItems: 1, maxItems: 3, description: Public HTTPS reference images., items: { type: string, format: uri, pattern: '^https://' } } + aspect_ratio: { type: string, enum: ['16:9', '9:16', auto], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [720p, 1080p, 4k, 4K], default: 720p, description: Output video resolution. 4k and 4K are equivalent. Price depends on quality and resolution. } + quality: { type: string, enum: [Fast, Lite], default: Fast, description: Reference-image generation tier. } + watermark: { type: string, description: Optional watermark text forwarded to the selected model. } + enable_translation: { type: boolean, description: Allow prompt translation before generation. } + Seedance25VideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: '`images` cannot be combined with any `reference_*` input. Audio-only reference generation is supported.' + properties: + model: { type: string, const: seedance-2.5, description: Must be `seedance-2.5`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs., items: { type: string, format: uri, pattern: '^https://' } } + reference_images: { type: array, minItems: 1, maxItems: 30, description: Public HTTPS image references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 10, description: Public HTTPS video references for multimodal reference generation., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 10, description: Public HTTPS audio references. Audio-only reference generation is supported., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 4, maximum: 30, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: [adaptive, '21:9', '16:9', '4:3', '1:1', '3:4', '9:16'], default: adaptive, description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p, 1080p], default: 720p, description: Output resolution tier. 480p and 1080p are priced separately; see the pricing page. } + generate_audio: { type: boolean, default: true, description: Generate synchronized audio with the video. } + seed: { type: integer, minimum: -1, maximum: 4294967295, default: -1, description: Reproducibility seed. Use -1 for a random seed. } + KlingShot: + type: object + additionalProperties: false + required: [prompt, duration] + properties: + prompt: { type: string, minLength: 1, maxLength: 500, description: Instructions for this shot. } + duration: { type: integer, minimum: 1, maximum: 12, description: Shot duration in seconds. All shot durations must sum to the task duration. } + KlingElement: + type: object + additionalProperties: false + required: [name, element_input_urls] + description: Use 2-4 image URLs or one video URL. A video element may include one audio URL and a 3-8 second segment in milliseconds. + properties: + name: { type: string, minLength: 1, description: Stable name used to reference this element in the prompt. } + description: { type: string, description: Optional description of the subject or object. } + element_input_urls: + type: array + minItems: 1 + maxItems: 4 + description: Two to four image URLs, or one video URL. + items: { type: string, format: uri, pattern: '^https://' } + element_input_audio_urls: + type: array + minItems: 1 + maxItems: 1 + description: Optional audio URL used with a video element. + items: { type: string, format: uri, pattern: '^https://' } + start_time: { type: integer, minimum: 0, maximum: 30000, description: Video element segment start time in milliseconds. } + end_time: { type: integer, minimum: 0, maximum: 30000, description: Video element segment end time in milliseconds. The segment must be 3-8 seconds. } + Kling3VideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: Multi-shot mode accepts one first-frame image, requires `multi_prompt`, and defaults sound to true. Shot durations must sum to `duration`. + properties: + model: { type: string, const: kling-3, description: Must be `kling-3`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 2, description: One first-frame image or first- and last-frame images as public HTTPS URLs. Multi-shot mode accepts exactly one., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 3, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: + type: string + enum: ['16:9', '9:16', '1:1'] + description: Defaults to 16:9 for text generation. Omit it with frame images to adapt to the input aspect ratio. + resolution: { type: string, enum: [std, pro, 4K], default: pro, description: Output quality tier. } + sound: { type: boolean, description: Generate synchronized sound. Defaults to true in multi-shot mode. } + multi_shots: { type: boolean, default: false, description: Enable storyboard-style multi-shot generation. } + multi_prompt: + type: array + minItems: 1 + maxItems: 5 + description: Shot definitions required when `multi_shots=true`. + items: { $ref: '#/components/schemas/KlingShot' } + elements: + type: array + maxItems: 3 + description: Up to three reusable subject or object references. + items: { $ref: '#/components/schemas/KlingElement' } + Kling26MotionControlVideoRequest: + type: object + additionalProperties: false + required: [model, images, reference_videos] + description: | + Transfer motion from one uploaded 3–30 second MP4/MOV video to one + uploaded character image. Both URLs must come from `/v1/files` for the + current BeatAPI account. BeatAPI detects the reference-video duration + server-side and reserves USD at the selected per-second rate, rounding + fractional seconds up. The image must be 10 MB or smaller; the video + may be up to 100 MB. + properties: + model: { type: string, const: kling-2.6-motion-control, description: Must be `kling-2.6-motion-control`. } + prompt: { type: string, maxLength: 2500, description: Optional motion or scene guidance. } + images: + type: array + minItems: 1 + maxItems: 1 + description: Exactly one character-image URL returned by the current account's `/v1/files` upload. + items: { type: string, format: uri, pattern: '^https://' } + reference_videos: + type: array + minItems: 1 + maxItems: 1 + description: Exactly one 3–30 second MP4/MOV URL returned by the current account's `/v1/files` upload. Its detected duration determines billing. + items: { type: string, format: uri, pattern: '^https://' } + resolution: { type: string, enum: [720p, 1080p], default: 720p, description: Output resolution and per-second price tier. } + character_orientation: { type: string, enum: [image, video], default: image, description: Image orientation supports motion videos up to 10 seconds; video orientation supports up to 30 seconds. } + Kling3MotionControlVideoRequest: + type: object + additionalProperties: false + required: [model, images, reference_videos] + description: | + Kling 3.0 motion transfer using exactly one uploaded image and one + uploaded 3–30 second MP4/MOV. Both assets must exceed 340 px in width + and height and use an aspect ratio from 2:5 to 5:2. BeatAPI detects the + reference-video duration server-side and reserves USD at the selected + per-second rate, rounding fractional seconds up. + properties: + model: { type: string, const: kling-3-motion-control, description: Must be `kling-3-motion-control`. } + prompt: { type: string, maxLength: 2500, description: Optional motion or scene guidance. } + images: + type: array + minItems: 1 + maxItems: 1 + description: Exactly one character-image URL returned by the current account's `/v1/files` upload; maximum 10 MB. + items: { type: string, format: uri, pattern: '^https://' } + reference_videos: + type: array + minItems: 1 + maxItems: 1 + description: Exactly one 3–30 second MP4/MOV URL returned by the current account's `/v1/files` upload; maximum 100 MB. Its detected duration determines billing. + items: { type: string, format: uri, pattern: '^https://' } + resolution: { type: string, enum: [720p, 1080p], default: 720p, description: Output resolution and per-second price tier. } + character_orientation: { type: string, enum: [image, video], default: image, description: Image orientation supports motion videos up to 10 seconds; video orientation supports up to 30 seconds. } + background_source: { type: string, enum: [input_video, input_image], default: input_video, description: Preserve the background from the motion video or character image. } + Wan30VideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: 'Renders 2–30 seconds in a single pass. `images` starts the render from a picture; reference videos and audio travel alongside it.' + properties: + model: { type: string, const: wan-3.0, description: Must be `wan-3.0`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 10, description: Public HTTPS images. One starts an image-to-video render; more are used as visual references., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 5, description: Public HTTPS video references. A request that carries one is billed at 1.5x., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 5, description: Public HTTPS audio references., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 2, maximum: 30, default: 5, description: Requested output duration in seconds. Any whole number in range; there is no long-clip surcharge. } + aspect_ratio: { type: string, enum: ['16:9', '9:16', '1:1', '4:3', '3:4'], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p, 1080p], default: 720p, description: Output resolution tier. Price scales with it. } + Wan30PrimeVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: 'Renders 2–30 seconds in a single pass. `images` starts the render from a picture; reference videos and audio travel alongside it.' + properties: + model: { type: string, const: wan-3.0-prime, description: Must be `wan-3.0-prime`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: Video generation instructions. } + images: { type: array, minItems: 1, maxItems: 10, description: Public HTTPS images. One starts an image-to-video render; more are used as visual references., items: { type: string, format: uri, pattern: '^https://' } } + reference_videos: { type: array, minItems: 1, maxItems: 5, description: Public HTTPS video references. A request that carries one is billed at 1.5x., items: { type: string, format: uri, pattern: '^https://' } } + reference_audios: { type: array, minItems: 1, maxItems: 5, description: Public HTTPS audio references., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 2, maximum: 30, default: 5, description: Requested output duration in seconds. Any whole number in range; there is no long-clip surcharge. } + aspect_ratio: { type: string, enum: ['16:9', '9:16', '1:1', '4:3', '3:4'], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [480p, 720p, 1080p], default: 720p, description: Output resolution tier. Price scales with it. } + HappyHorse10VideoRequest: + type: object + additionalProperties: false + required: [model, prompt, images] + description: 'Image to video only — this model publishes no text-to-video mode, so `images` is required.' + properties: + model: { type: string, const: happyhorse-1.0, description: Must be `happyhorse-1.0`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: 'What should happen in the shot — the motion, the expression, the camera.' } + images: { type: array, minItems: 1, maxItems: 9, description: Source images as public HTTPS URLs. At least one is required., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 3, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: ['16:9', '9:16', '1:1', '4:3', '3:4'], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [720p, 1080p], default: 720p, description: Output resolution tier. Price scales with it. } + HappyHorse11VideoRequest: + type: object + additionalProperties: false + required: [model, prompt, images] + description: 'Image to video only — this model publishes no text-to-video mode, so `images` is required.' + properties: + model: { type: string, const: happyhorse-1.1, description: Must be `happyhorse-1.1`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: 'What should happen in the shot — the motion, the expression, the camera.' } + images: { type: array, minItems: 1, maxItems: 9, description: Source images as public HTTPS URLs. At least one is required., items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 3, maximum: 15, default: 5, description: Requested output duration in seconds. } + aspect_ratio: { type: string, enum: ['16:9', '9:16', '1:1', '4:3', '3:4'], default: '16:9', description: Output video aspect ratio. } + resolution: { type: string, enum: [720p, 1080p], default: 720p, description: Output resolution tier. Price scales with it. } + MinimaxH3MaxVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: 'Text to video, or image to video when `images` carries a first frame. A second image becomes the last frame. Output tops out at 768P — MiniMax H3 renders 2K for less per second.' + properties: + model: { type: string, const: minimax-h3-max, description: Must be `minimax-h3-max`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: 'What should happen in the shot — the motion, the expression, the camera.' } + images: { type: array, minItems: 1, maxItems: 2, description: 'Public HTTPS images. One starts the render from a first frame; a second becomes the last frame, in first-to-last order.', items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 1, default: 5, description: 'Requested output duration in seconds. Billed per second at the rate for the chosen resolution. If a duration is unsupported, the API returns `400`.' } + resolution: { type: string, enum: ['480P', '768P'], default: '768P', description: 'Output resolution. Upper-case P, and the only two values this model accepts. Price scales with it.' } + seed: { type: integer, minimum: 0, description: Reuse a seed to re-render the same motion. A random seed is chosen when omitted. } + MinimaxH3MaxTurboVideoRequest: + type: object + additionalProperties: false + required: [model, prompt] + description: 'H3 Max on a faster stack — the same request contract and the same modes, roughly 2.5x quicker, at half the per-second rate.' + properties: + model: { type: string, const: minimax-h3-max-turbo, description: Must be `minimax-h3-max-turbo`. } + prompt: { type: string, minLength: 1, maxLength: 5000, description: 'What should happen in the shot — the motion, the expression, the camera.' } + images: { type: array, minItems: 1, maxItems: 2, description: 'Public HTTPS images. One starts the render from a first frame; a second becomes the last frame, in first-to-last order.', items: { type: string, format: uri, pattern: '^https://' } } + duration: { type: integer, minimum: 1, default: 5, description: 'Requested output duration in seconds. Billed per second at the rate for the chosen resolution. If a duration is unsupported, the API returns `400`.' } + resolution: { type: string, enum: ['480P', '768P'], default: '768P', description: 'Output resolution. Upper-case P, and the only two values this model accepts. Price scales with it.' } + seed: { type: integer, minimum: 0, description: Reuse a seed to re-render the same motion. A random seed is chosen when omitted. } TaskResponse: type: object required: [data] properties: data: $ref: '#/components/schemas/Task' + description: Accepted or current BeatAPI task state. Usage: type: object - required: [object, credit_balance, total_tasks, credits_settled, credits_refunded, concurrency, by_workflow] + required: [object, credit_balance, total_tasks, credits_settled, credits_refunded, concurrency, by_workflow, by_capability, by_model, by_api_key] properties: object: type: string enum: [usage] credit_balance: - type: integer - description: Current credit balance. It may be negative. + type: number + format: double + multipleOf: 0.01 + description: Current USD balance. The compatibility field name is retained; 1 Credit equals $1 USD. The balance may be negative. total_tasks: type: integer credits_settled: - type: integer + type: number + format: double + multipleOf: 0.01 credits_refunded: - type: integer + type: number + format: double + multipleOf: 0.01 concurrency: type: object required: [limit, active] @@ -437,20 +1289,68 @@ components: example: 2 active: type: integer - description: Active processing tasks currently using BeatAPI processing resources. Music Video storyboard_ready and requires_action tasks can have settled credits without counting toward this value. + description: Active processing tasks currently using BeatAPI processing resources. Music Video storyboard_ready and requires_action tasks can have settled USD usage without counting toward this value. by_workflow: type: array + description: Compatibility view containing workflow tasks only. Image, video, and Effect tasks are reported under by_capability instead. items: type: object required: [workflow, tasks, credits_settled] properties: workflow: type: string - enum: [music-video, ecommerce-video] + enum: [music-video, ecommerce-video, video-analysis] tasks: type: integer credits_settled: + type: number + format: double + multipleOf: 0.01 + by_capability: + type: array + items: + type: object + required: [task_kind, capability_id, tasks, credits_settled] + properties: + task_kind: + type: string + enum: [workflow, effect, image, video] + capability_id: + type: string + tasks: type: integer + credits_settled: + type: number + format: double + multipleOf: 0.01 + by_model: + type: array + items: + type: object + required: [media_type, model, tasks, credits_settled] + properties: + media_type: + type: string + enum: [image, video] + model: + type: string + tasks: + type: integer + credits_settled: + type: number + format: double + multipleOf: 0.01 + by_api_key: + type: array + items: + type: object + required: [api_key_id, title, key_prefix, tasks, credits_settled] + properties: + api_key_id: { type: string } + title: { type: string } + key_prefix: { type: string } + tasks: { type: integer } + credits_settled: { type: number, format: double, multipleOf: 0.01 } realtime: type: object required: [sessions, credits, active] @@ -459,8 +1359,10 @@ components: type: integer description: Total BeatAPI realtime sessions for this account. credits: - type: integer - description: Credits settled by connected realtime sessions. + type: number + format: double + multipleOf: 0.01 + description: USD amount settled by connected realtime sessions. active: type: integer description: Realtime sessions in ready, connecting, or active state. @@ -470,6 +1372,238 @@ components: properties: data: $ref: '#/components/schemas/Usage' + VideoAnalysisTaskCreateRequest: + type: object + additionalProperties: false + required: [video_url, prompt] + properties: + video_url: + type: string + format: uri + description: BeatAPI-hosted MP4 or MOV input URL returned by POST /v1/files for the current account. Maximum verified duration is 600 seconds. + prompt: + type: string + minLength: 1 + maxLength: 12000 + description: Analysis instruction. Ask for timestamped output when temporal precision matters. + analysis_depth: + type: string + enum: [standard, deep] + default: standard + description: Standard is the default low-cost route; deep uses the higher-reasoning route. + max_output_tokens: + type: integer + minimum: 256 + maximum: 8192 + default: 2048 + description: Requested answer budget. Provider-reported output usage can include hidden reasoning tokens above this value; BeatAPI records the variance for audit and settles actual reported usage within the task reservation. + MusicVideoTaskCreateRequest: + oneOf: + - $ref: '#/components/schemas/StandardMusicVideoTaskCreateRequest' + - $ref: '#/components/schemas/PremiumMusicVideoTaskCreateRequest' + discriminator: + propertyName: mv_tier + mapping: + standard: '#/components/schemas/StandardMusicVideoTaskCreateRequest' + premium: '#/components/schemas/PremiumMusicVideoTaskCreateRequest' + StandardMusicVideoTaskCreateRequest: + type: object + required: [images, audio_url] + allOf: + - if: + required: [lip_sync] + properties: + lip_sync: { const: true } + then: + required: [lip_ref_url] + properties: + lip_ref_url: {} + not: + anyOf: + - required: [mv_mode] + properties: { mv_mode: {} } + - required: [lip_ref_urls] + properties: { lip_ref_urls: {} } + properties: + mv_tier: + type: string + enum: [standard] + default: standard + description: May be omitted to preserve the backwards-compatible Standard contract. + images: + type: array + minItems: 1 + maxItems: 7 + description: Standard scene images. Provide 1-7 public HTTPS PNG, JPEG, or WebP URLs; place the primary subject or opening scene first. Upload local files through `POST /v1/files` and use the returned `data.url`. + items: { type: string, format: uri } + audio_url: + type: string + format: uri + description: Public HTTPS audio URL; Standard audio must be 10-180 seconds. + prompt: { type: string, maxLength: 3000, description: "Optional creative direction for story, setting, performance, camera, lighting, and pacing. Maximum 3000 characters." } + language: { type: string, enum: [en, zh], description: Dialogue and lyric language used by the Standard workflow. } + quality: { type: string, enum: [standard, high], default: standard, description: Generation quality tier. High quality is unavailable at 540p. } + style: { type: string, maxLength: 200, description: "Optional concise visual style, such as cinematic, anime, documentary, or fashion editorial." } + aspect_ratio: { type: string, enum: ['1:1', '16:9', '9:16', '4:3', '3:4'], description: Target output placement. Set explicitly for the destination player or social feed. } + resolution: { type: string, enum: [540p, 720p, 1080p], default: 720p, description: Output resolution. 540p cannot be combined with high quality or lip sync. } + lip_sync: + type: boolean + default: false + description: Generate lip-synchronized performance. When true, `lip_ref_url` is required. + lip_ref_url: + type: string + format: uri + description: Public HTTPS close-up, front-facing face image used for Standard lip sync. + add_subtitle: { type: boolean, default: false, description: Burn generated or supplied subtitles into the final video. } + subtitle_color: { type: string, pattern: '^#[0-9A-Fa-f]{6}$', example: '#FFFFFF', description: Subtitle text color as a six-digit hexadecimal value. Used when subtitles are enabled. } + srt_url: { type: string, format: uri, description: Optional public HTTPS `.srt` subtitle file. Upload a local subtitle through `POST /v1/files`. } + duration: + type: integer + minimum: 10 + maximum: 180 + description: Billing fallback only; detected audio duration wins. + compose_mode: + type: string + enum: [auto, manual] + default: auto + description: Auto composes the final Music Video; manual pauses at `requires_action` so shots can be reviewed or edited before compose. + PremiumMusicVideoTaskCreateRequest: + allOf: + - oneOf: + - $ref: '#/components/schemas/PremiumMusicVideoSingTaskCreateRequest' + - $ref: '#/components/schemas/PremiumMusicVideoSingPerformTaskCreateRequest' + - $ref: '#/components/schemas/PremiumMusicVideoDanceTaskCreateRequest' + - $ref: '#/components/schemas/PremiumMusicVideoPerformTaskCreateRequest' + discriminator: + propertyName: mv_mode + mapping: + sing: '#/components/schemas/PremiumMusicVideoSingTaskCreateRequest' + sing_perform: '#/components/schemas/PremiumMusicVideoSingPerformTaskCreateRequest' + dance: '#/components/schemas/PremiumMusicVideoDanceTaskCreateRequest' + perform: '#/components/schemas/PremiumMusicVideoPerformTaskCreateRequest' + PremiumMusicVideoTaskRequestBase: + type: object + required: [mv_tier, mv_mode, audio_url] + not: + anyOf: + - required: [quality] + properties: { quality: {} } + - required: [language] + properties: { language: {} } + - required: [lip_sync] + properties: { lip_sync: {} } + - required: [lip_ref_url] + properties: { lip_ref_url: {} } + - required: [srt_url] + properties: { srt_url: {} } + - required: [compose_mode] + properties: { compose_mode: {} } + properties: + mv_tier: { type: string, enum: [premium], description: Selects the Premium Music Video workflow and its mode-specific inputs. } + mv_mode: { type: string, enum: [sing, sing_perform, dance, perform], description: Premium performance mode. Sing modes require `lip_ref_urls`; dance and perform require exactly six `images`. } + audio_url: + type: string + format: uri + description: Public HTTPS audio URL; Premium audio must be 10-300 seconds. + prompt: { type: string, maxLength: 3000, description: "Optional creative direction for story, setting, performance, camera, lighting, and pacing. Maximum 3000 characters." } + style: { type: string, maxLength: 200 } + aspect_ratio: { type: string, enum: ['1:1', '16:9', '9:16', '4:3', '3:4'], description: Target output placement. Set explicitly for the destination player or social feed. } + resolution: + type: string + enum: [720p] + default: 720p + description: Premium output is fixed to 720p. + add_subtitle: { type: boolean, default: false, description: Burn generated subtitles into the final video. } + subtitle_color: { type: string, pattern: '^#[0-9A-Fa-f]{6}$', example: '#FFFFFF', description: Subtitle text color as a six-digit hexadecimal value. Used when subtitles are enabled. } + duration: + type: integer + minimum: 10 + maximum: 300 + description: Premium billing fallback only; detected audio duration wins. + PremiumMusicVideoSingTaskCreateRequest: + allOf: + - $ref: '#/components/schemas/PremiumMusicVideoTaskRequestBase' + - type: object + required: [lip_ref_urls] + properties: + mv_mode: { type: string, enum: [sing] } + images: + type: array + minItems: 0 + maxItems: 6 + description: Optional Premium scene images for sing mode. Provide up to six public HTTPS PNG, JPEG, or WebP URLs. + items: { type: string, format: uri } + lip_ref_urls: + type: array + minItems: 1 + maxItems: 2 + description: Required for sing mode. One or two public HTTPS close-up, front-facing face images for lip synchronization. + items: { type: string, format: uri } + PremiumMusicVideoSingPerformTaskCreateRequest: + allOf: + - $ref: '#/components/schemas/PremiumMusicVideoTaskRequestBase' + - type: object + required: [lip_ref_urls] + properties: + mv_mode: { type: string, enum: [sing_perform] } + images: + type: array + minItems: 0 + maxItems: 6 + description: Optional Premium scene images for sing and perform mode. Provide up to six public HTTPS PNG, JPEG, or WebP URLs. + items: { type: string, format: uri } + lip_ref_urls: + type: array + minItems: 1 + maxItems: 2 + description: Required for sing and perform mode. One or two public HTTPS close-up, front-facing face images for lip synchronization. + items: { type: string, format: uri } + PremiumMusicVideoDanceTaskCreateRequest: + allOf: + - $ref: '#/components/schemas/PremiumMusicVideoTaskRequestBase' + - type: object + required: [images] + not: + required: [lip_ref_urls] + properties: { lip_ref_urls: {} } + properties: + mv_mode: { type: string, enum: [dance] } + images: + type: array + minItems: 6 + maxItems: 6 + description: Required for dance mode. Provide exactly six public HTTPS PNG, JPEG, or WebP scene images. + items: { type: string, format: uri } + PremiumMusicVideoPerformTaskCreateRequest: + allOf: + - $ref: '#/components/schemas/PremiumMusicVideoTaskRequestBase' + - type: object + required: [images] + not: + required: [lip_ref_urls] + properties: { lip_ref_urls: {} } + properties: + mv_mode: { type: string, enum: [perform] } + images: + type: array + minItems: 6 + maxItems: 6 + description: Required for perform mode. Provide exactly six public HTTPS PNG, JPEG, or WebP scene images. + items: { type: string, format: uri } + EditMusicVideoShotRequest: + type: object + additionalProperties: false + required: [prompt] + properties: + prompt: + type: string + maxLength: 3000 + images: + type: array + minItems: 0 + maxItems: 6 + description: Premium tasks only. Optional replacement scene images; an empty array is treated as omitted. Standard tasks reject this field. + items: { type: string, format: uri } RealtimeSession: type: object required: [id, object, status, expires_at, max_duration_seconds, allowed_origins, credits, request_id, created_at, connected_at, closed_at] @@ -477,41 +1611,66 @@ components: id: type: string pattern: '^rts_' + description: Stable Realtime Session ID used to inspect or close the session. object: type: string enum: [realtime.session] + description: Object discriminator; always `realtime.session`. status: type: string enum: [ready, connecting, active, closed, failed, expired] description: Active means BeatAPI accepted the first billing heartbeat after remote output began. - client_secret: - type: string - description: Returned only by POST. Give this short-lived BeatAPI secret to the browser SDK; never give the browser an sk_ API key. - pattern: '^brt_live_' expires_at: type: string format: date-time + description: Time when the unconnected short-lived session credential expires. max_duration_seconds: type: integer enum: [15, 60, 300] + description: Maximum selected live duration and billing tier in seconds. allowed_origins: type: array - items: { type: string, format: uri } + description: Exact browser origins authorized to use this Session. + items: + type: string + format: uri + pattern: '^(https://[A-Za-z0-9.-]+(?::[0-9]+)?|http://(?:localhost|127\\.0\\.0\\.1|\\[::1\\])(?::[0-9]+)?)$' + description: Exact browser origin. Use HTTPS in production; HTTP is accepted only for localhost development. + example: https://app.example.com credits: type: object required: [reserved, settled, refunded] + description: USD reservation, settlement, and refund lifecycle for this Realtime Session. Compatibility field names are retained. properties: - reserved: { type: integer } - settled: { type: integer } - refunded: { type: integer } + reserved: { type: number, format: double, multipleOf: 0.01, description: USD amount reserved when the Session is created. } + settled: { type: number, format: double, multipleOf: 0.01, description: USD amount settled after the first accepted billing heartbeat. } + refunded: { type: number, format: double, multipleOf: 0.01, description: USD amount refunded if the Session ends without billing activation. } request_id: type: string - created_at: { type: string, format: date-time } + description: Correlation ID to retain for logs and BeatAPI support. + created_at: { type: string, format: date-time, description: Time when the Session was created. } connected_at: type: [string, 'null'] format: date-time description: Time of the first accepted BeatAPI billing heartbeat; null before billing activation. - closed_at: { type: [string, 'null'], format: date-time } + closed_at: { type: [string, 'null'], format: date-time, description: "Time when the Session closed, or null while it remains open." } + RealtimeSessionCreated: + allOf: + - $ref: '#/components/schemas/RealtimeSession' + - type: object + required: [client_secret] + properties: + client_secret: + type: string + description: Short-lived BeatAPI browser credential returned only by POST. Never expose an sk_ API key to the browser. + pattern: '^brt_live_' + RealtimeSessionCreateResponse: + type: object + required: [data] + properties: + data: + $ref: '#/components/schemas/RealtimeSessionCreated' + description: Created Realtime Session including the one-time short-lived browser credential. RealtimeSessionResponse: type: object required: [data] @@ -524,6 +1683,7 @@ components: properties: data: $ref: '#/components/schemas/File' + description: Uploaded file metadata and the public HTTPS URL to use in later requests. WebhookEndpointList: type: object required: [object, data] @@ -547,6 +1707,7 @@ components: properties: data: $ref: '#/components/schemas/WebhookEndpoint' + description: Created or retrieved webhook endpoint. Public API responses return the full signing secret at creation and mask it afterward; authenticated dashboard owners can explicitly reveal it again. DeleteResponse: type: object required: [data] @@ -565,10 +1726,12 @@ components: properties: error: type: object + description: Structured BeatAPI error. Use `code` for program logic and retain `request_id` for support. required: [code, message, request_id] properties: code: type: string + description: Stable machine-readable error code. enum: - bad_request - unauthorized @@ -578,6 +1741,7 @@ components: - idempotency_conflict - user_concurrency_exceeded - rate_limit_exceeded + - content_policy_violation - processing_unavailable - processing_failed - processing_timeout @@ -592,8 +1756,10 @@ components: - internal_error message: type: string + description: Human-readable detail intended for logs and debugging. request_id: type: string + description: Correlation ID to retain for BeatAPI support. retry_after_seconds: type: integer description: Present on retryable rate-limit or capacity responses when the client should wait before retrying. @@ -637,7 +1803,214 @@ components: message: Too many polling requests. Poll every 5-10 seconds. request_id: req_xxx retry_after_seconds: 12 + InternalError: + description: BeatAPI could not complete the request because of an internal or storage failure. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: + code: internal_error + message: Internal error. Contact support with the request_id if the problem continues. + request_id: req_xxx + ProcessingUnavailable: + description: BeatAPI processing is temporarily unavailable or did not complete within the processing window. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: + code: processing_unavailable + message: Task processing is temporarily unavailable. + request_id: req_xxx paths: + /v1/models: + get: + operationId: listTextModels + tags: [Text] + summary: List available text models + description: Returns the text models currently enabled for this BeatAPI environment in OpenAI list format. + security: + - BearerAuth: [] + - ApiKeyHeader: [] + - GoogleApiKeyHeader: [] + responses: + '200': + description: OpenAI-compatible model list + content: + application/json: + schema: { $ref: '#/components/schemas/TextModelList' } + example: + object: list + data: + - id: gpt-5.6-sol + object: model + created: 1788220800 + owned_by: beatapi + - id: gpt-5.6-terra + object: model + created: 1788220800 + owned_by: beatapi + - id: gpt-5.6-luna + object: model + created: 1788220800 + owned_by: beatapi + - id: claude-fable-5-1 + object: model + created: 1788220800 + owned_by: beatapi + '401': { description: Invalid or missing BeatAPI API key } + '404': { description: Text API is not enabled for this environment } + '429': { description: Request rate limit exceeded } + + /v1/responses: + post: + operationId: createTextResponse + tags: [Text] + summary: Create a text response + description: Recommended OpenAI-compatible surface for reasoning, tools, structured outputs, and streaming. + security: + - BearerAuth: [] + - ApiKeyHeader: [] + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughRequest' } + example: + model: gpt-5.6-sol + input: Design a resilient webhook retry strategy for a payments API. + reasoning: { effort: medium } + stream: true + responses: + '200': + description: OpenAI-compatible JSON response or server-sent event stream + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughResponse' } + text/event-stream: + schema: { type: string } + '401': { description: Invalid or missing BeatAPI API key } + '402': { description: Insufficient BeatAPI USD balance } + '429': { description: Rate limit or settlement backlog } + '502': { description: Text gateway could not complete the request } + '503': { description: Text service is temporarily unavailable } + + /v1/chat/completions: + post: + operationId: createChatCompletion + tags: [Text] + summary: Create a text chat completion + description: OpenAI Chat Completions-compatible endpoint for existing SDK integrations. + security: + - BearerAuth: [] + - ApiKeyHeader: [] + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughRequest' } + example: + model: gpt-5.6-terra + messages: + - role: user + content: Summarize the attached product requirements into an implementation plan. + stream: true + responses: + '200': + description: OpenAI-compatible JSON response or server-sent event stream + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughResponse' } + text/event-stream: + schema: { type: string } + '401': { description: Invalid or missing BeatAPI API key } + '402': { description: Insufficient BeatAPI USD balance } + '429': { description: Rate limit or settlement backlog } + '502': { description: Text gateway could not complete the request } + '503': { description: Text service is temporarily unavailable } + + /v1/messages: + post: + operationId: createMessage + tags: [Text] + summary: Create an Anthropic-compatible message + description: Anthropic Messages-compatible endpoint. Send the BeatAPI key with x-api-key or Bearer authentication. + security: + - ApiKeyHeader: [] + - BearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughRequest' } + example: + model: gpt-5.6-luna + max_tokens: 1024 + messages: + - role: user + content: Classify this support request and return JSON. + responses: + '200': + description: Anthropic-compatible JSON response or server-sent event stream + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughResponse' } + text/event-stream: + schema: { type: string } + '401': { description: Invalid or missing BeatAPI API key } + '402': { description: Insufficient BeatAPI USD balance } + '429': { description: Rate limit or settlement backlog } + '502': { description: Text gateway could not complete the request } + '503': { description: Text service is temporarily unavailable } + + /v1beta/models/{model}:{action}: + post: + operationId: generateGeminiCompatibleContent + tags: [Text] + summary: Generate text content with a Gemini-compatible request + description: Gemini-compatible endpoint for generateContent and streamGenerateContent. The BeatAPI key is removed before forwarding. + security: + - GoogleApiKeyHeader: [] + - GoogleApiKeyQuery: [] + - BearerAuth: [] + parameters: + - in: path + name: model + required: true + schema: { $ref: '#/components/schemas/TextModelId' } + - in: path + name: action + required: true + schema: { type: string, enum: [generateContent, streamGenerateContent] } + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: true + example: + contents: + - role: user + parts: + - text: Explain this architecture decision in three concise bullets. + responses: + '200': + description: Gemini-compatible JSON response or server-sent event stream + content: + application/json: + schema: { $ref: '#/components/schemas/TextPassthroughResponse' } + text/event-stream: + schema: { type: string } + '401': { description: Invalid or missing BeatAPI API key } + '402': { description: Insufficient BeatAPI USD balance } + '429': { description: Rate limit or settlement backlog } + '502': { description: Text gateway could not complete the request } + '503': { description: Text service is temporarily unavailable } + /v1/workflows: get: operationId: listWorkflows @@ -646,53 +2019,596 @@ paths: summary: List launch workflows security: [] responses: - '200': - description: Workflow list + '200': + description: Workflow list + content: + application/json: + schema: + $ref: '#/components/schemas/WorkflowListResponse' + example: + data: + object: list + data: + - id: music-video + object: workflow + name: Music Video API + description: Generate short music video clips from audio, lyrics, and visual direction. + - id: ecommerce-video + object: workflow + name: Ecommerce Video API + description: Generate product ad videos from product images and a short creative brief. + - id: video-analysis + object: workflow + name: Video Analysis API + description: Analyze an uploaded video with timestamp-aware multimodal reasoning. + '429': + $ref: '#/components/responses/RateLimited' + + /v1/media/models: + get: + operationId: listGenerationModels + tags: [Generation] + summary: List BeatAPI image and video generation models + description: Returns stable BeatAPI model aliases and public input modes. Internal execution routing is not part of this contract. + security: [] + parameters: + - in: query + name: media_type + schema: { type: string, enum: [image, video] } + responses: + '200': + description: Generation model list + content: + application/json: + schema: { $ref: '#/components/schemas/GenerationModelListResponse' } + '400': { $ref: '#/components/responses/BadRequest' } + '429': { $ref: '#/components/responses/RateLimited' } + + /v1/images/tasks: + post: + operationId: createImageGenerationTask + tags: [Generation] + summary: Create an image generation task + description: | + Creates one asynchronous image task. Select the model-specific request + contract with `model`, save the returned `data.id`, and poll + `GET /v1/tasks/{task_id}` until the task succeeds or fails. + security: [{ BearerAuth: [] }] + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string, maxLength: 255 } + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/ImageGenerationTaskCreateRequest' } + examples: + Nano Banana: + summary: Nano Banana + value: + model: nano-banana + prompt: Editorial product photograph on a warm stone pedestal. + aspect_ratio: '1:1' + output_format: png + Nano Banana 2: + summary: Nano Banana 2 at 2K + value: + model: nano-banana-2 + prompt: Editorial campaign image with crisp product typography. + aspect_ratio: '4:5' + resolution: 2K + output_format: png + Nano Banana 2 Lite: + summary: Nano Banana 2 Lite at 1K + value: + model: nano-banana-2-lite + prompt: Fast social product visual on a clean studio background. + aspect_ratio: '1:1' + output_format: jpeg + Nano Banana Pro: + summary: Nano Banana Pro + value: + model: nano-banana-pro + prompt: Place the supplied product in a premium editorial studio scene. + images: ['https://media.beatapi.io/samples/smart-bottle.png'] + aspect_ratio: '4:5' + resolution: 2K + output_format: png + GPT Image 2: + summary: GPT Image 2 + value: + model: gpt-image-2 + prompt: Create a clean campaign image from the supplied product reference. + images: ['https://media.beatapi.io/samples/smart-bottle.png'] + aspect_ratio: '1:1' + resolution: 2K + Seedream 5 Pro: + summary: Seedream 5 Pro + value: + model: seedream-5-pro + prompt: Recompose the product as a cinematic storefront campaign image. + images: ['https://media.beatapi.io/samples/smart-bottle.png'] + Grok Imagine Image 2.0: + summary: Grok Imagine Image 2.0 + value: + model: grok-imagine-image-2.0 + prompt: Turn the supplied product into a bold launch campaign visual. + images: ['https://media.beatapi.io/samples/smart-bottle.png'] + aspect_ratio: '16:9' + responses: + '201': + description: Image generation task accepted + content: + application/json: + schema: { $ref: '#/components/schemas/TaskResponse' } + examples: + Nano Banana Pro: + summary: Nano Banana Pro + value: + data: + id: task_8K2qA + object: task + task_kind: image + capability_id: nano-banana-pro + capability_version: null + media_type: image + model: nano-banana-pro + status: queued + stage: queued + created_at: 1782210000 + updated_at: 1782210000 + completed_at: null + output: null + usage: + credits_reserved: 0.09 + credits_charged: 0.09 + credits_settled: 0 + credits_refunded: 0 + request_id: req_abc123 + error_code: null + error_message: null + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '402': { description: Insufficient USD balance, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } } + '409': { description: Idempotency key conflicts with another request body, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } } + '429': { $ref: '#/components/responses/RateLimited' } + + /v1/videos/tasks: + post: + operationId: createVideoGenerationTask + tags: [Generation] + summary: Create a video generation task + description: | + Creates one asynchronous video task. Select the model-specific request + contract with `model`, save the returned `data.id`, and poll + `GET /v1/tasks/{task_id}` until the task succeeds or fails. + security: [{ BearerAuth: [] }] + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string, maxLength: 255 } + requestBody: + required: true + content: + application/json: + schema: { $ref: '#/components/schemas/VideoGenerationTaskCreateRequest' } + examples: + MiniMax H3: + summary: MiniMax H3 + value: + model: minimax-h3 + prompt: A slow cinematic push through a misty mountain village at dawn. + duration: 5 + aspect_ratio: '16:9' + resolution: 768P + Seedance 2: + summary: Seedance 2 + value: + model: seedance-2 + prompt: A handheld tracking shot through a crowded neon night market. + duration: 8 + aspect_ratio: '16:9' + resolution: 1080p + generate_audio: true + Seedance 2 Fast: + summary: Seedance 2 Fast + value: + model: seedance-2-fast + prompt: A fast cinematic production draft through a neon night market. + duration: 5 + aspect_ratio: '16:9' + resolution: 720p + generate_audio: true + Seedance 2 Mini: + summary: Seedance 2 Mini + value: + model: seedance-2-mini + prompt: A low-cost storyboard draft for a product reveal. + duration: 5 + aspect_ratio: '16:9' + resolution: 720p + Veo 3.1 Quality: + summary: Veo 3.1 Quality + value: + model: veo-3.1 + prompt: A cinematic aerial reveal of a quiet coastal village at sunrise. + aspect_ratio: '16:9' + resolution: 720p + quality: Quality + Veo 3.1 Fast: + summary: Veo 3.1 Fast + value: + model: veo-3.1 + prompt: A fast cinematic product reveal with natural camera motion. + aspect_ratio: '16:9' + resolution: 1080p + quality: Fast + Veo 3.1 Lite: + summary: Veo 3.1 Lite + value: + model: veo-3.1 + prompt: A concise storyboard-ready product reveal. + aspect_ratio: '16:9' + resolution: 4k + quality: Lite + Veo 3.1 Reference Fast: + summary: Veo 3.1 Reference Fast + value: + model: veo-3.1 + prompt: Create a cohesive cinematic scene using the supplied visual references. + reference_images: + - https://media.beatapi.io/samples/neon-singer.png + - https://media.beatapi.io/samples/smart-bottle.png + aspect_ratio: '16:9' + resolution: 720p + quality: Fast + Seedance 2.5: + summary: Seedance 2.5 + value: + model: seedance-2.5 + prompt: A cinematic tracking shot through a rain-lit night market. + duration: 5 + aspect_ratio: '16:9' + resolution: 720p + generate_audio: true + seed: -1 + Kling 3: + summary: Kling 3 + value: + model: kling-3 + prompt: A dramatic product reveal with a slow orbiting camera move. + duration: 5 + aspect_ratio: '16:9' + resolution: pro + sound: true + Kling 2.6 Motion Control: + summary: Kling 2.6 Motion Control + value: + model: kling-2.6-motion-control + prompt: Keep the character identity stable while following the reference motion. + images: + - https://media.beatapi.io/inputs/character.png + reference_videos: + - https://media.beatapi.io/inputs/motion.mp4 + resolution: 720p + character_orientation: video + Kling 3.0 Motion Control: + summary: Kling 3.0 Motion Control + value: + model: kling-3-motion-control + prompt: Preserve the character and transfer the full-body motion precisely. + images: + - https://media.beatapi.io/inputs/character.png + reference_videos: + - https://media.beatapi.io/inputs/motion.mp4 + resolution: 1080p + character_orientation: image + background_source: input_video + responses: + '201': + description: Video generation task accepted + content: + application/json: + schema: { $ref: '#/components/schemas/TaskResponse' } + examples: + MiniMax H3: + summary: MiniMax H3 + value: + data: + id: task_8K2qA + object: task + task_kind: video + capability_id: minimax-h3 + capability_version: null + media_type: video + model: minimax-h3 + status: queued + stage: queued + created_at: 1782210000 + updated_at: 1782210000 + completed_at: null + output: null + usage: + credits_reserved: 0.2 + credits_charged: 0.2 + billable_duration_seconds: 5 + credits_settled: 0 + credits_refunded: 0 + request_id: req_abc123 + error_code: null + error_message: null + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '402': { description: Insufficient USD balance, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } } + '409': { description: Idempotency key conflicts with another request body, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } } + '429': { $ref: '#/components/responses/RateLimited' } + + /v1/effects: + get: + operationId: listEffects + tags: [Effects] + summary: List active Effects + security: [] + description: Returns only versioned Effects that have passed BeatAPI publication gates. Internal integration names, template ids, costs, and execution context are never exposed. + parameters: + - in: query + name: output_type + schema: { type: string, enum: [image, video] } + - in: query + name: category + schema: { type: string } + responses: + '200': + description: Active Effect catalog + content: + application/json: + schema: { $ref: '#/components/schemas/EffectListResponse' } + '400': { $ref: '#/components/responses/BadRequest' } + '429': { $ref: '#/components/responses/RateLimited' } + + /v1/effects/{effect_id}: + get: + operationId: getEffect + tags: [Effects] + summary: Get an active Effect + security: [] + parameters: + - in: path + name: effect_id + required: true + schema: { type: string } + responses: + '200': + description: Effect definition and immutable current version contract + content: + application/json: + schema: { $ref: '#/components/schemas/EffectResponse' } + '404': + description: Effect is unknown or not currently published. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + + /v1/effects/tasks: + post: + operationId: createEffectTask + tags: [Effects] + summary: Create an Effect task + security: [{ BearerAuth: [] }] + description: | + Creates an asynchronous image or video Effect task. Read the catalog + first: image count, accepted input types, output resolution/duration, + and execution contract are fixed by the selected Effect version. Send + an `Idempotency-Key`; an exact replay returns the + accepted task before remote input URLs are revalidated, while a changed + body returns `idempotency_conflict`. + + The USD amount is reserved atomically when accepted, settled on success, and + fully refunded after a definite processing failure. An uncertain create + result is not blindly retried and never switches integrations automatically. + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string, maxLength: 255 } + requestBody: + required: true + content: + application/json: + schema: + type: object + additionalProperties: false + required: [effect_id, images] + properties: + effect_id: { type: string, example: video-muscle-max, description: Stable published Effect ID from `GET /v1/effects`. } + effect_version: + type: integer + minimum: 1 + description: Optional immutable version. Omit to use the current published version. + images: + type: array + minItems: 1 + maxItems: 7 + description: Public HTTPS input images in the order required by the selected Effect version. Read `GET /v1/effects/{effect_id}` for the exact count and accepted media rules; upload local files with `POST /v1/files`. + items: { type: string, format: uri } + options: + type: object + additionalProperties: false + description: Optional controls supported by the selected Effect version. Omit unsupported controls; the catalog is the source of truth. + properties: + aspect_ratio: { type: string, description: Requested output aspect ratio when the selected Effect exposes this option. } + resolution: { type: string, description: Requested output resolution when the selected Effect exposes this option. } + duration: { type: integer, description: Requested video duration in seconds when the selected Effect exposes this option. } + bgm: { type: boolean, description: Include background music when supported by the selected Effect. } + seed: { type: integer, description: Optional deterministic seed when supported by the selected Effect. } + example: + effect_id: video-muscle-max + images: ['https://media.beatapi.io/samples/portrait.png'] + options: { resolution: 720p, duration: 12 } + responses: + '201': + description: Effect task accepted + content: + application/json: + schema: { $ref: '#/components/schemas/TaskResponse' } + example: + data: + id: task_effect123 + object: task + task_kind: effect + capability_id: video-muscle-max + capability_version: 1 + effect_id: video-muscle-max + effect_version: 1 + status: queued + stage: queued + created_at: 1782210000 + updated_at: 1782210000 + completed_at: null + output: null + usage: { credits_reserved: 1.2, credits_charged: 1.2, credits_settled: 0, credits_refunded: 0 } + request_id: req_effect123 + error_code: null + error_message: null + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '402': + description: Insufficient USD balance. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + '404': + description: Effect or requested version is unavailable. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + '409': + description: Idempotency key conflicts with another request body. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + '429': { $ref: '#/components/responses/RateLimited' } + + /v1/video-analysis/tasks: + post: + operationId: createVideoAnalysisTask + tags: [Video Analysis] + x-apidog-folder: Video Analysis API + summary: Analyze an uploaded video + security: + - BearerAuth: [] + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string } + description: Optional retry key. Reusing the same key with the same normalized request returns the accepted task. + example: video-analysis-cus_123-01 + description: | + Analyze one MP4 or MOV previously uploaded with `POST /v1/files` by the + current BeatAPI account. `standard` is the default low-cost route; + `deep` uses the higher-reasoning route. BeatAPI reserves an estimate from + verified video duration and the output budget, then settles from actual + input and output token usage. Standard costs $0.36 per 1M input tokens and + $1.60 per 1M output tokens; Deep costs $0.72 per 1M input tokens and $5.00 + per 1M output tokens. Each completed task is rounded up to the nearest + $0.01 because the shared USD balance settles in cents. Save `data.id` and poll the shared Task + endpoint if the task remains queued for processing capacity. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VideoAnalysisTaskCreateRequest' + examples: + standard: + summary: Timestamped standard analysis + value: + video_url: https://media.beatapi.io/uploads/input.mp4 + prompt: Return a timestamped action summary and identify every scene transition. + analysis_depth: standard + max_output_tokens: 2048 + deep: + summary: Deep motion review + value: + video_url: https://media.beatapi.io/uploads/input.mp4 + prompt: Inspect body motion, contact, continuity, and fast transitions with timestamps. + analysis_depth: deep + max_output_tokens: 4096 + responses: + '201': + description: Analysis accepted for asynchronous processing. content: application/json: schema: - $ref: '#/components/schemas/WorkflowListResponse' + $ref: '#/components/schemas/TaskResponse' example: data: - object: list - data: - - id: music-video - object: workflow - name: Music Video API - description: Generate short music video clips from audio, lyrics, and visual direction. - - id: ecommerce-video - object: workflow - name: Ecommerce Video API - description: Generate product ad videos from product images and a short creative brief. - '429': - $ref: '#/components/responses/RateLimited' + id: task_va8K2qA + object: task + task_kind: workflow + capability_id: video-analysis + capability_version: 1 + workflow: video-analysis + status: queued + stage: queued + created_at: 1787385600 + updated_at: 1787385600 + completed_at: null + output: null + usage: + credits_reserved: 0.01 + credits_charged: 0.01 + billable_duration_seconds: 60 + credits_settled: 0 + credits_refunded: 0 + request_id: req_va123 + error_code: null + error_message: null + '400': { $ref: '#/components/responses/BadRequest' } + '401': { $ref: '#/components/responses/Unauthorized' } + '402': + description: Account balance is not sufficient for the reserved analysis envelope. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + '409': + description: Idempotency key conflicts with another request body. + content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } + '429': { $ref: '#/components/responses/RateLimited' } /v1/music-video/tasks: post: operationId: createMusicVideoTask tags: [Music Video] x-apidog-folder: Music Video API/Create Video - summary: Create Music Video + summary: Create a Music Video workflow task security: - BearerAuth: [] + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string } + description: Optional retry key. Reusing the same key with the same request body returns the accepted task; reusing it with a different body returns `409 idempotency_conflict`. + example: mv-create-cus_123-01 description: | - Music Video requires public HTTPS image URLs and a public HTTPS audio URL. - Prompt, language, quality, style, lip reference, subtitle, and format - controls are optional. BeatAPI detects the audio duration before task - creation and charges the detected billable duration at the selected - per-second customer-credit rate. If audio duration cannot be detected, - `duration` is used as the billing fallback. + Music Video requires public HTTPS media URLs. Requests that omit + `mv_tier` use `standard` and preserve the existing API behavior. + Premium retains the configured integration host but uses a distinct + execution path selected internally by BeatAPI. + Shared controls include prompt, aspect ratio, subtitles, and the tier's + billing fallback. Language, quality, `lip_sync`, `lip_ref_url`, + `srt_url`, and `compose_mode` are Standard-only. Premium uses `mv_mode` + plus `style` and mode-specific images or `lip_ref_urls`. BeatAPI detects audio duration before task + creation and records the billable duration in Task usage. If audio + duration cannot be detected, `duration` is used as the billing fallback. Input limits: - - Images must contain 1-7 public HTTPS URLs. + - Standard images must contain 1-7 public HTTPS URLs. + - Premium `sing` and `sing_perform` accept 0-6 scene images and require 1-2 `lip_ref_urls`. + - Premium `dance` and `perform` require exactly 6 scene images. - Use png, jpg, jpeg, or webp images; each image should be 50 MB or smaller. - Image aspect ratio should be between 1:4 and 4:1. - - Audio must be a public HTTPS mp3, wav, aac, or m4a URL between 10 and 180 seconds. + - Standard audio must be 10-180 seconds; Premium audio must be 10-300 seconds and contain vocals or lyrics rather than instrumental-only audio. - The audio file should be 50 MB or smaller. - `prompt` is optional and must be at most 3000 characters. - - `lip_ref_url`, when provided, must be a public HTTPS image URL. Use a clear, front-facing close-up face reference for best lip-sync results. - - `srt_url`, when provided, must point to an `.srt` subtitle file. - - `duration` is only a billing fallback when BeatAPI cannot detect the audio length; it must be 10-180 seconds and cannot override a detected audio duration. + - Standard `lip_sync=true` requires `lip_ref_url`. It must be a public HTTPS image URL showing a clear, front-facing close-up face. + - Standard `srt_url`, when provided, must point to an `.srt` subtitle file. + - `duration` is only a billing fallback when BeatAPI cannot detect the audio length; Standard accepts 10-180 seconds and Premium accepts 10-300 seconds. It cannot override a detected audio duration. BeatAPI validates URL shape, text limits, enum values, combination limits, and audio duration at task creation. Files uploaded through @@ -700,15 +2616,6 @@ paths: Third-party media URLs must follow the same media requirements and may be rejected during processing if invalid. - Customer pricing: - - MV 540p standard: 4 credits/s - - MV 720p standard: 5 credits/s - - MV 1080p standard: 6 credits/s - - lip_sync add-on: +2 credits/s - - MV 720p high: 16 credits/s - - MV 1080p high: 18 credits/s - - Ecommerce Video 1080p: 15 credits/s - Combination limits: - `quality=high` is not supported with `resolution=540p`. - `lip_sync=true` is not supported with `resolution=540p`. @@ -724,76 +2631,23 @@ paths: content: application/json: schema: - type: object - required: [images, audio_url] - properties: - images: - type: array - minItems: 1 - maxItems: 7 - description: 1-7 public HTTPS image URLs. Use png, jpg, jpeg, or webp images; each image should be 50 MB or smaller, with aspect ratio from 1:4 to 4:1. /v1/files uploads are checked before use; third-party URLs may be rejected during processing if invalid. - items: - type: string - format: uri - audio_url: - type: string - format: uri - description: Public HTTPS audio URL. Use mp3, wav, aac, or m4a; file size should be 50 MB or smaller and duration must be 10-180 seconds. - prompt: - type: string - maxLength: 3000 - description: Optional creative prompt, at most 3000 characters. - language: - type: string - enum: [en, zh] - lip_sync: - type: boolean - lip_ref_url: - type: string - format: uri - description: Public HTTPS image URL for lip-sync face reference. Use a clear, front-facing close-up face reference. - style: - type: string - maxLength: 200 - description: Optional style phrase, at most 200 characters. - quality: - type: string - enum: [standard, high] - default: standard - aspect_ratio: - type: string - enum: ['1:1', '16:9', '9:16', '4:3', '3:4'] - resolution: - type: string - enum: [540p, 720p, 1080p] - default: 720p - add_subtitle: - type: boolean - subtitle_color: - type: string - pattern: '^#[0-9A-Fa-f]{6}$' - example: '#FFFFFF' - srt_url: - type: string - format: uri - duration: - type: integer - minimum: 10 - maximum: 180 - description: Billing fallback when audio duration cannot be detected. It must be 10-180 seconds and cannot override a detected audio duration. - compose_mode: - type: string - enum: [auto, manual] - default: auto - example: - images: - - https://media.beatapi.io/samples/neon-singer.png - audio_url: https://media.beatapi.io/samples/neon-singer-preview.mp3 - prompt: Neon rooftop performance with metro cutaways and cinematic light trails. - language: en - quality: standard - resolution: 720p - compose_mode: auto + $ref: '#/components/schemas/MusicVideoTaskCreateRequest' + examples: + standard_backwards_compatible: + summary: Standard music video + value: + mv_tier: standard + images: ['https://media.beatapi.io/samples/neon-singer.png'] + audio_url: https://media.beatapi.io/samples/neon-singer-preview.mp3 + resolution: 720p + premium_sing: + value: { mv_tier: premium, mv_mode: sing, images: [], lip_ref_urls: ['https://media.beatapi.io/samples/singer.png'], audio_url: 'https://media.beatapi.io/samples/song.mp3', resolution: 720p } + premium_sing_perform: + value: { mv_tier: premium, mv_mode: sing_perform, images: ['https://media.beatapi.io/samples/stage.png'], lip_ref_urls: ['https://media.beatapi.io/samples/singer.png'], audio_url: 'https://media.beatapi.io/samples/song.mp3', resolution: 720p } + premium_dance: + value: { mv_tier: premium, mv_mode: dance, images: ['https://media.beatapi.io/1.png', 'https://media.beatapi.io/2.png', 'https://media.beatapi.io/3.png', 'https://media.beatapi.io/4.png', 'https://media.beatapi.io/5.png', 'https://media.beatapi.io/6.png'], audio_url: 'https://media.beatapi.io/samples/song.mp3', resolution: 720p } + premium_perform: + value: { mv_tier: premium, mv_mode: perform, images: ['https://media.beatapi.io/1.png', 'https://media.beatapi.io/2.png', 'https://media.beatapi.io/3.png', 'https://media.beatapi.io/4.png', 'https://media.beatapi.io/5.png', 'https://media.beatapi.io/6.png'], audio_url: 'https://media.beatapi.io/samples/song.mp3', resolution: 720p } responses: '201': description: Task accepted @@ -801,28 +2655,34 @@ paths: application/json: schema: $ref: '#/components/schemas/TaskResponse' - example: - data: - id: task_8K2qA - object: task - workflow: music-video - status: queued - stage: queued - storyboard: - shots: [] - created_at: 1782210000 - updated_at: 1782210000 - completed_at: null - output: null - usage: - credits_reserved: 75 - credits_charged: 75 - billable_duration_seconds: 15 - credits_settled: 0 - credits_refunded: 0 - request_id: req_abc123 - error_code: null - error_message: null + examples: + standard_backwards_compatible: + summary: Standard music video + value: + data: + id: task_8K2qA + object: task + task_kind: workflow + capability_id: music-video + capability_version: 1 + workflow: music-video + status: queued + stage: queued + storyboard: + shots: [] + created_at: 1782210000 + updated_at: 1782210000 + completed_at: null + output: null + usage: + credits_reserved: 1.5 + credits_charged: 1.5 + billable_duration_seconds: 15 + credits_settled: 0 + credits_refunded: 0 + request_id: req_abc123 + error_code: null + error_message: null '400': $ref: '#/components/responses/BadRequest' '401': @@ -838,6 +2698,17 @@ paths: code: insufficient_credits message: Account balance is not sufficient for this task. request_id: req_xxx + '409': + description: The Idempotency-Key was reused with a different body or while another request with that key is still being processed. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: + code: idempotency_conflict + message: This Idempotency-Key was already used with a different request body. + request_id: req_xxx '429': description: User concurrency exceeded. content: @@ -855,13 +2726,16 @@ paths: operationId: editMusicVideoShot tags: [Music Video] x-apidog-folder: Music Video API/Advanced Editing - summary: Edit Shot + summary: Edit a Music Video storyboard shot security: - BearerAuth: [] description: | Edit one storyboard shot using its BeatAPI `shot_id`. This operation - charges BeatAPI customer credits using the selected quality/resolution - rate and the shot duration. Default shot duration is 5 seconds. + charges the customer USD balance using the applicable task tier and shot + duration. Standard edits accept only `prompt`. Premium edits accept + `prompt` plus up to 6 optional replacement `images`. Generation quality, + resolution, and shot duration are inherited from the original task and + are not editable request fields. When the edit finishes, BeatAPI stores the edited shot media and exposes it on that shot. The existing final Music Video is not replaced until you call compose with the selected shot ids. @@ -878,35 +2752,21 @@ paths: schema: type: string example: shot_xxx + - in: header + name: Idempotency-Key + required: false + schema: { type: string } + description: Optional retry key. Reusing the same key for this task, shot, and request body returns the accepted task without charging the USD amount again; changing any of them returns `409 idempotency_conflict`. + example: music-edit-task_8K2qA-shot_xxx-01 requestBody: required: true content: application/json: schema: - type: object - required: [prompt] - properties: - prompt: - type: string - maxLength: 3000 - duration: - type: integer - minimum: 1 - maximum: 180 - default: 5 - quality: - type: string - enum: [standard, high] - default: standard - resolution: - type: string - enum: [540p, 720p, 1080p] - default: 720p + $ref: '#/components/schemas/EditMusicVideoShotRequest' example: prompt: Night city chorus with brighter face lighting. - duration: 5 - quality: standard - resolution: 720p + images: ['https://media.beatapi.io/samples/stage.png'] responses: '202': description: Shot edit accepted @@ -918,19 +2778,37 @@ paths: $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' + '402': + description: Account balance is not sufficient for this shot edit. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The Idempotency-Key was reused for a different task, shot, or request body, or the same request is still being processed. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': description: Task or shot not found. content: application/json: schema: $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' + '502': + $ref: '#/components/responses/ProcessingUnavailable' /v1/music-video/tasks/{task_id}/shots/{shot_id}/media: post: operationId: getMusicVideoShotMedia tags: [Music Video] x-apidog-folder: Music Video API/Advanced Editing - summary: Get Shot Media + summary: Retrieve a Music Video storyboard shot media URL security: - BearerAuth: [] description: | @@ -989,18 +2867,24 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' + '502': + $ref: '#/components/responses/ProcessingUnavailable' /v1/music-video/tasks/{task_id}/compose: post: operationId: composeMusicVideoTask tags: [Music Video] x-apidog-folder: Music Video API/Advanced Editing - summary: Compose Video + summary: Compose a Music Video task from selected shots security: - BearerAuth: [] description: | Compose selected BeatAPI storyboard shots into the final Music Video. - This operation charges a fixed 1 BeatAPI customer credit. + This operation charges a fixed $1 USD. parameters: - in: path name: task_id @@ -1008,6 +2892,12 @@ paths: schema: type: string example: task_8K2qA + - in: header + name: Idempotency-Key + required: false + schema: { type: string } + description: Optional retry key. Reusing the same key for this task and request body returns the accepted task without charging the $1 compose amount again; changing either returns `409 idempotency_conflict`. + example: music-compose-task_8K2qA-01 requestBody: required: true content: @@ -1034,22 +2924,51 @@ paths: $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' + '402': + description: Account balance is not sufficient for this compose operation. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '409': + description: The Idempotency-Key was reused for a different task or request body, or the same request is still being processed. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': description: Task or shot not found. content: application/json: schema: $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' + '502': + $ref: '#/components/responses/ProcessingUnavailable' /v1/ecommerce-video/tasks: post: operationId: createEcommerceVideoTask tags: [Ecommerce Video] x-apidog-folder: Ecommerce Video API - summary: Create Ecommerce Video + summary: Create an Ecommerce Video workflow task security: - BearerAuth: [] - description: Ecommerce Video requires product images and an explicit output duration. + parameters: + - in: header + name: Idempotency-Key + required: false + schema: { type: string } + description: Optional retry key. Reusing the same key with the same request body returns the accepted task; reusing it with a different body returns `409 idempotency_conflict`. + example: ecommerce-create-cus_123-01 + description: | + Ecommerce Video creates a complete product video from public HTTPS product or + lifestyle images, an explicit duration, and optional creative direction. Upload + local images with `POST /v1/files`, save the returned Task ID, and poll + `GET /v1/tasks/{task_id}` until the task succeeds or fails. requestBody: required: true content: @@ -1062,6 +2981,7 @@ paths: type: array minItems: 1 maxItems: 7 + description: Primary product or scene image first, followed by up to six additional public HTTPS PNG, JPEG, or WebP product or lifestyle images. Upload local files with `POST /v1/files` and use the returned `data.url`. items: type: string format: uri @@ -1069,21 +2989,25 @@ paths: type: integer minimum: 10 maximum: 60 + description: Required target output duration in seconds and the basis for USD calculation. Allowed range is 10-60 seconds. prompt: type: string maxLength: 2000 + description: Optional creative direction, audience, product benefit, offer, tone, scenes, or call to action. Maximum 2000 characters. aspect_ratio: type: string enum: ['16:9', '9:16', '1:1'] + description: Target output placement. Use 16:9 for landscape, 9:16 for vertical social, or 1:1 for square placements; set explicitly for stable layout. language: type: string enum: [en, zh] + description: Dialogue and narration language. Use `en` for English or `zh` for Chinese; set explicitly when the prompt contains mixed languages. example: images: - https://media.beatapi.io/samples/smart-bottle.png duration: 15 prompt: Fast product launch ad for paid social. - aspect_ratio: 9:16 + aspect_ratio: '9:16' responses: '201': description: Task accepted @@ -1095,6 +3019,9 @@ paths: data: id: task_p9Lm2 object: task + task_kind: workflow + capability_id: ecommerce-video + capability_version: 1 workflow: ecommerce-video status: queued stage: queued @@ -1103,8 +3030,8 @@ paths: completed_at: null output: null usage: - credits_reserved: 225 - credits_charged: 225 + credits_reserved: 4.5 + credits_charged: 4.5 billable_duration_seconds: 15 credits_settled: 0 credits_refunded: 0 @@ -1126,6 +3053,17 @@ paths: code: insufficient_credits message: Account balance is not sufficient for this task. request_id: req_xxx + '409': + description: The Idempotency-Key was reused with a different body or while another request with that key is still being processed. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + example: + error: + code: idempotency_conflict + message: This Idempotency-Key was already used with a different request body. + request_id: req_xxx '429': description: User concurrency exceeded. content: @@ -1168,6 +3106,9 @@ paths: data: id: task_8K2qA object: task + task_kind: workflow + capability_id: music-video + capability_version: 1 workflow: music-video status: queued stage: queued @@ -1178,8 +3119,8 @@ paths: completed_at: null output: null usage: - credits_reserved: 75 - credits_charged: 75 + credits_reserved: 1.5 + credits_charged: 1.5 billable_duration_seconds: 15 credits_settled: 0 credits_refunded: 0 @@ -1192,6 +3133,9 @@ paths: data: id: task_8K2qA object: task + task_kind: workflow + capability_id: music-video + capability_version: 1 workflow: music-video status: storyboard_ready stage: storyboard_ready @@ -1210,10 +3154,10 @@ paths: updated_at: 1782210300 output: null usage: - credits_reserved: 75 - credits_charged: 75 + credits_reserved: 1.5 + credits_charged: 1.5 billable_duration_seconds: 15 - credits_settled: 75 + credits_settled: 1.5 credits_refunded: 0 request_id: req_abc123 error_code: null @@ -1224,6 +3168,9 @@ paths: data: id: task_8K2qA object: task + task_kind: workflow + capability_id: music-video + capability_version: 1 workflow: music-video status: failed stage: failed @@ -1234,11 +3181,11 @@ paths: completed_at: 1782210600 output: null usage: - credits_reserved: 75 - credits_charged: 75 + credits_reserved: 1.5 + credits_charged: 1.5 billable_duration_seconds: 15 credits_settled: 0 - credits_refunded: 75 + credits_refunded: 1.5 request_id: req_abc123 error_code: processing_timeout error_message: Task waited too long for platform capacity. @@ -1262,9 +3209,9 @@ paths: security: - BearerAuth: [] description: | - Reserve credits and allocate a short-lived BeatAPI realtime session. Send a unique + Reserve the selected USD amount and allocate a short-lived BeatAPI realtime session. Send a unique `Idempotency-Key`; retries with the same user, key, and body return the same session - and deterministic short-lived `client_secret` without reserving credits or capacity + and deterministic short-lived `client_secret` without reserving funds or capacity twice. The browser receives only that BeatAPI secret and connects with `@beatapi/realtime`. @@ -1280,7 +3227,8 @@ paths: - in: header name: Idempotency-Key required: true - schema: { type: string, maxLength: 128 } + schema: { type: string, minLength: 1, maxLength: 128 } + example: rts-create-cus_123-01 requestBody: required: true content: @@ -1292,14 +3240,22 @@ paths: max_duration_seconds: type: integer enum: [15, 60, 300] + description: Required maximum live session duration in seconds. The USD amount is reserved for the selected 15, 60, or 300 second tier. allowed_origins: type: array minItems: 1 maxItems: 10 - items: { type: string, format: uri } + description: Exact browser origins allowed to use the short-lived session secret. + items: + type: string + format: uri + pattern: '^(https://[A-Za-z0-9.-]+(?::[0-9]+)?|http://(?:localhost|127\\.0\\.0\\.1|\\[::1\\])(?::[0-9]+)?)$' + description: Exact browser origin. Use HTTPS in production; HTTP is accepted only for localhost development. + example: https://app.example.com metadata: type: object maxProperties: 20 + description: Optional server-defined string metadata for your own correlation. Up to 20 keys; keys are at most 64 characters and values at most 256 characters. propertyNames: { maxLength: 64 } additionalProperties: { type: string, maxLength: 256 } example: @@ -1311,11 +3267,25 @@ paths: description: Realtime session created content: application/json: - schema: { $ref: '#/components/schemas/RealtimeSessionResponse' } + schema: { $ref: '#/components/schemas/RealtimeSessionCreateResponse' } + example: + data: + id: rts_8K2qA + object: realtime.session + status: ready + client_secret: brt_live_example_short_lived_secret + expires_at: '2026-08-12T10:01:00.000Z' + max_duration_seconds: 60 + allowed_origins: ['https://app.example.com'] + credits: { reserved: 1.2, settled: 0, refunded: 0 } + request_id: req_abc123 + created_at: '2026-08-12T10:00:00.000Z' + connected_at: null + closed_at: null '400': { $ref: '#/components/responses/BadRequest' } '401': { $ref: '#/components/responses/Unauthorized' } '402': - description: Insufficient credits + description: Insufficient USD balance content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } '409': description: Idempotency conflict @@ -1379,24 +3349,44 @@ paths: example: data: object: usage - credit_balance: 1080 + credit_balance: 21.6 total_tasks: 12 - credits_settled: 720 - credits_refunded: 450 + credits_settled: 14.4 + credits_refunded: 9 concurrency: limit: 2 active: 1 realtime: sessions: 3 - credits: 90 + credits: 1.8 active: 1 by_workflow: - workflow: music-video tasks: 8 - credits_settled: 480 + credits_settled: 9.6 - workflow: ecommerce-video tasks: 4 - credits_settled: 240 + credits_settled: 4.8 + by_capability: + - task_kind: image + capability_id: seedream-5-pro + tasks: 3 + credits_settled: 0.42 + - task_kind: video + capability_id: veo-3.1 + tasks: 2 + credits_settled: 14 + by_model: + - media_type: image + model: seedream-5-pro + tasks: 3 + credits_settled: 0.42 + by_api_key: + - api_key_id: key_abc123 + title: Production + key_prefix: sk_live_abcd + tasks: 12 + credits_settled: 14.4 '401': $ref: '#/components/responses/Unauthorized' @@ -1409,28 +3399,43 @@ paths: security: - BearerAuth: [] description: | - Use file upload when your images, audio, or subtitle files are not + Use file upload when your images, videos, audio, or subtitle files are not already hosted at public HTTPS URLs. The returned HTTPS URL can be used - directly in `images`, `audio_url`, or `srt_url`. + directly in `images`, `reference_videos`, `audio_url`, or `srt_url`. Limits: - - Maximum file size is 50 MB. + - Images, audio, and subtitles: maximum 50 MB. + - Motion videos: maximum 100 MB. - Images: `png`, `jpg`, `jpeg`, `webp` (`image/png`, `image/jpeg`, `image/webp`). - Audio: `mp3`, `wav`, `aac`, `m4a` (`audio/mpeg`, `audio/wav`, `audio/aac`, `audio/mp4`). - - Audio uploads must be 10-180 seconds. + - Audio uploads must be 10-300 seconds. The selected Music Video tier applies its own task limit: Standard 10-180 seconds; Premium 10-300 seconds. + - Motion videos: `mp4`, `mov` (`video/mp4`, `video/quicktime`), 3-30 seconds. Duration and dimensions are detected during upload. - Subtitles: `srt` (`application/x-subrip`; multipart uploads may use `text/plain` only when the filename ends in `.srt`). - - PDF, generic text files, octet-stream uploads, videos, and zip files + - PDF, generic text files, octet-stream uploads, Matroska videos, and zip files are not supported for launch. - Send either multipart form-data with a `file` field, or send the raw file body with the asset `Content-Type`. + - `Content-Length` is required and is validated before BeatAPI buffers + the request body; chunked uploads without a declared length are rejected. - Returned URLs are HTTPS and long-lived for launch. - Uploaded audio files are duration-checked during upload. The response includes `audio_duration_seconds` when the uploaded asset is audio. + - Uploaded videos are signature-, duration-, and dimension-checked. The + response includes `video_duration_seconds`, `width`, and `height`. - Workflow task inputs still require public HTTPS URLs. Localhost, private network URLs, and data URLs are not accepted. + parameters: + - in: header + name: Content-Length + required: true + description: Exact request-body length in bytes. For multipart uploads this includes multipart framing overhead. + schema: + type: integer + minimum: 1 + maximum: 105906176 requestBody: required: true content: @@ -1445,6 +3450,26 @@ paths: purpose: type: string enum: [input] + image/png: + schema: { type: string, format: binary } + image/jpeg: + schema: { type: string, format: binary } + image/webp: + schema: { type: string, format: binary } + audio/mpeg: + schema: { type: string, format: binary } + audio/wav: + schema: { type: string, format: binary } + audio/aac: + schema: { type: string, format: binary } + audio/mp4: + schema: { type: string, format: binary } + video/mp4: + schema: { type: string, format: binary } + video/quicktime: + schema: { type: string, format: binary } + application/x-subrip: + schema: { type: string, format: binary } responses: '201': description: File uploaded @@ -1468,6 +3493,10 @@ paths: $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' /v1/webhooks: get: @@ -1499,6 +3528,10 @@ paths: updated_at: 1782210000 '401': $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' post: operationId: createWebhookEndpoint tags: [Webhooks] @@ -1507,8 +3540,9 @@ paths: security: - BearerAuth: [] description: | - The signing secret is returned only once at creation time. Store it - securely. Later responses return a masked secret. + The public API returns the signing secret in full at creation time. Store + it securely; later public API responses return a masked secret. An + authenticated dashboard owner can explicitly reveal the secret again. BeatAPI sends these headers with each delivery: - `x-beatapi-event`: `task.succeeded` or `task.failed` @@ -1572,8 +3606,9 @@ paths: ``` Reject old timestamps to prevent replay attacks. A 5 minute window is - recommended. Failed deliveries are retried at most 3 times with fixed - backoff windows of 1 minute, 5 minutes, and 15 minutes. Polling + recommended. A delivery is attempted at most 3 times total: the initial + request plus up to 2 retries, with fixed backoff windows of 1 minute and + 5 minutes. Polling `GET /v1/tasks/{task_id}` remains the source of truth. requestBody: required: true @@ -1586,10 +3621,14 @@ paths: url: type: string format: uri + pattern: '^https://' + description: Public HTTPS callback URL that accepts BeatAPI task events. Do not use localhost or a private-network URL. description: type: string + description: Optional internal label for identifying the endpoint in your account. events: type: array + description: Task events to deliver. Omit to subscribe to both `task.succeeded` and `task.failed`. items: type: string enum: [task.succeeded, task.failed] @@ -1618,6 +3657,10 @@ paths: $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' /v1/webhooks/{id}: get: @@ -1660,6 +3703,10 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' patch: operationId: updateWebhookEndpoint tags: [Webhooks] @@ -1684,6 +3731,8 @@ paths: url: type: string format: uri + pattern: '^https://' + description: Public HTTPS callback URL that accepts BeatAPI task events. Do not use localhost or a private-network URL. description: type: string status: @@ -1707,6 +3756,16 @@ paths: $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' + '404': + description: Webhook endpoint not found. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' delete: operationId: deleteWebhookEndpoint tags: [Webhooks] @@ -1734,3 +3793,13 @@ paths: deleted: true '401': $ref: '#/components/responses/Unauthorized' + '404': + description: Webhook endpoint not found. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/InternalError' diff --git a/contract/contract.lock.json b/contract/contract.lock.json index 47af603..2ccc3fe 100644 --- a/contract/contract.lock.json +++ b/contract/contract.lock.json @@ -1,6 +1,6 @@ { "source": "https://github.com/BeatAPI/beatapi-examples", - "ref": "8f7d3cff33445ded4d3c94f0fb8ac5060d790148", + "ref": "83a139a123a3139cf53a362132b7b1d8a0066e1f", "openapiVersion": "1.0.0-launch", - "sha256": "290100dba10bb14b040f5a826657ad7d4a01f179fc28ef69ea0bdcaa66f7dad3" + "sha256": "bcd8dfb2124e7815ea52e513c99a2522e749316fc41f02a6f70957d3e3ebe293" } diff --git a/package-lock.json b/package-lock.json index e56282d..4c88566 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1067,9 +1067,9 @@ "license": "MIT" }, "node_modules/@redocly/openapi-core": { - "version": "1.34.18", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.18.tgz", - "integrity": "sha512-UyKIm0wTPw5BcY7Z2PkbK1Ma260um96LSBWXHrdSMe+ZV0EPMyDfAcUcjjm3qEiGST9OK/1TriekdPCZkn4Q3A==", + "version": "1.34.19", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.19.tgz", + "integrity": "sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==", "dev": true, "license": "MIT", "dependencies": { @@ -1078,7 +1078,7 @@ "colorette": "1.4.0", "https-proxy-agent": "7.0.6", "js-levenshtein": "1.1.6", - "js-yaml": "4.3.0", + "js-yaml": "4.3.1", "minimatch": "5.1.9", "pluralize": "8.0.0", "yaml-ast-parser": "0.0.43" @@ -1404,9 +1404,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, "funding": [ { diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 30d3f2f..c0edccf 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -2,6 +2,7 @@ import { BeatAPIError } from "./errors.js"; import type { components, operations } from "./types.generated.js"; export type BeatAPIWorkflow = components["schemas"]["Workflow"]; +export type BeatAPITextModel = components["schemas"]["TextModel"]; export type BeatAPITaskStatus = components["schemas"]["TaskStatus"]; export type BeatAPITask = components["schemas"]["Task"]; export type BeatAPIUsage = components["schemas"]["Usage"]; @@ -9,6 +10,8 @@ export type BeatAPIFile = components["schemas"]["File"]; export type BeatAPIShotMedia = components["schemas"]["ShotMedia"]; export type BeatAPIWebhook = components["schemas"]["WebhookEndpoint"]; export type BeatAPIRealtimeSession = components["schemas"]["RealtimeSession"]; +export type BeatAPIGenerationModel = components["schemas"]["GenerationModel"]; +export type BeatAPIEffect = components["schemas"]["Effect"]; export type BeatAPIDeleteResult = components["schemas"]["DeleteResponse"]["data"]; export type MusicVideoTaskInput = @@ -25,6 +28,17 @@ export type UpdateWebhookInput = operations["updateWebhookEndpoint"]["requestBody"]["content"]["application/json"]; export type CreateRealtimeSessionInput = operations["createRealtimeSession"]["requestBody"]["content"]["application/json"]; +export type TextResponseInput = + operations["createTextResponse"]["requestBody"]["content"]["application/json"]; +export type TextResponseOutput = components["schemas"]["TextPassthroughResponse"]; +export type VideoAnalysisTaskInput = + operations["createVideoAnalysisTask"]["requestBody"]["content"]["application/json"]; +export type ImageGenerationTaskInput = + operations["createImageGenerationTask"]["requestBody"]["content"]["application/json"]; +export type VideoGenerationTaskInput = + operations["createVideoGenerationTask"]["requestBody"]["content"]["application/json"]; +export type CreateEffectTaskInput = + operations["createEffectTask"]["requestBody"]["content"]["application/json"]; type FetchLike = ( input: string | URL | Request, @@ -50,6 +64,7 @@ interface RequestOptions { body?: unknown | undefined; headers?: HeadersInit | undefined; authenticated?: boolean | undefined; + responseShape?: "beatapi" | "raw" | undefined; retry?: RetryOptions | undefined; } @@ -223,7 +238,11 @@ export class BeatAPIClient { }); const payload = await readPayload(response); - if (response.ok) return unwrapData(payload); + if (response.ok) { + return options.responseShape === "raw" + ? (payload as T) + : unwrapData(payload); + } const error = errorFromResponse(response, payload); if ( @@ -276,6 +295,84 @@ export class BeatAPIClient { ).then((result) => result.data); } + listTextModels(): Promise { + return this.request<{ object: "list"; data: BeatAPITextModel[] }>( + "/v1/models", + { responseShape: "raw" }, + ).then((result) => result.data); + } + + listGenerationModels(): Promise { + return this.request<{ object: "list"; data: BeatAPIGenerationModel[] }>( + "/v1/media/models", + { authenticated: false }, + ).then((result) => result.data); + } + + createImageTask(input: ImageGenerationTaskInput): Promise { + return this.request("/v1/images/tasks", { method: "POST", body: input }); + } + + createVideoTask(input: VideoGenerationTaskInput): Promise { + return this.request("/v1/videos/tasks", { method: "POST", body: input }); + } + + listEffects( + filters: { outputType?: "image" | "video"; category?: string } = {}, + ): Promise { + const query = new URLSearchParams(); + if (filters.outputType) query.set("output_type", filters.outputType); + if (filters.category) query.set("category", filters.category); + const suffix = query.size > 0 ? `?${query.toString()}` : ""; + return this.request<{ object: "list"; data: BeatAPIEffect[] }>( + `/v1/effects${suffix}`, + { authenticated: false }, + ).then((result) => result.data); + } + + getEffect(effectId: string): Promise { + return this.request(`/v1/effects/${encodePathSegment(effectId)}`, { + authenticated: false, + }); + } + + createEffectTask( + input: CreateEffectTaskInput, + options: { idempotencyKey: string }, + ): Promise { + const idempotencyKey = options.idempotencyKey.trim(); + if (!idempotencyKey) { + throw new TypeError("idempotencyKey must not be empty."); + } + return this.request("/v1/effects/tasks", { + method: "POST", + body: input, + headers: { "idempotency-key": idempotencyKey }, + }); + } + + createTextResponse(input: TextResponseInput): Promise { + return this.request("/v1/responses", { + method: "POST", + body: input, + responseShape: "raw", + }); + } + + createVideoAnalysisTask( + input: VideoAnalysisTaskInput, + options: { idempotencyKey?: string } = {}, + ): Promise { + const idempotencyKey = options.idempotencyKey?.trim(); + return this.request("/v1/video-analysis/tasks", { + method: "POST", + body: input, + ...(idempotencyKey + ? { headers: { "idempotency-key": idempotencyKey } } + : {}), + }); + } + getUsage(): Promise { return this.request("/v1/usage"); } diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 9aaa491..cae98da 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -2,9 +2,12 @@ export { BeatAPIClient, type BeatAPIClientOptions, type BeatAPIDeleteResult, + type BeatAPIEffect, type BeatAPIFile, + type BeatAPIGenerationModel, type BeatAPIRealtimeSession, type BeatAPIShotMedia, + type BeatAPITextModel, type BeatAPITask, type BeatAPITaskStatus, type BeatAPIUsage, @@ -12,13 +15,19 @@ export { type BeatAPIWorkflow, type CreateWebhookInput, type CreateRealtimeSessionInput, + type CreateEffectTaskInput, type EcommerceVideoTaskInput, + type ImageGenerationTaskInput, type MusicVideoComposeInput, type MusicVideoShotEditInput, type MusicVideoTaskInput, type RetryOptions, + type TextResponseInput, + type TextResponseOutput, type UpdateWebhookInput, type UploadFileOptions, + type VideoAnalysisTaskInput, + type VideoGenerationTaskInput, type WaitForTaskOptions, } from "./client.js"; export { BeatAPIError, type BeatAPIErrorOptions } from "./errors.js"; diff --git a/packages/client/src/types.generated.ts b/packages/client/src/types.generated.ts index d030b36..1b93586 100644 --- a/packages/client/src/types.generated.ts +++ b/packages/client/src/types.generated.ts @@ -4,6 +4,106 @@ */ export interface paths { + "/v1/models": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List available text models + * @description Returns the text models currently enabled for this BeatAPI environment in OpenAI list format. + */ + get: operations["listTextModels"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/responses": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create a text response + * @description Recommended OpenAI-compatible surface for reasoning, tools, structured outputs, and streaming. + */ + post: operations["createTextResponse"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/chat/completions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create a text chat completion + * @description OpenAI Chat Completions-compatible endpoint for existing SDK integrations. + */ + post: operations["createChatCompletion"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/messages": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create an Anthropic-compatible message + * @description Anthropic Messages-compatible endpoint. Send the BeatAPI key with x-api-key or Bearer authentication. + */ + post: operations["createMessage"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1beta/models/{model}:{action}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Generate text content with a Gemini-compatible request + * @description Gemini-compatible endpoint for generateContent and streamGenerateContent. The BeatAPI key is removed before forwarding. + */ + post: operations["generateGeminiCompatibleContent"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/workflows": { parameters: { query?: never; @@ -21,6 +121,164 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/media/models": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List BeatAPI image and video generation models + * @description Returns stable BeatAPI model aliases and public input modes. Internal execution routing is not part of this contract. + */ + get: operations["listGenerationModels"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/images/tasks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create an image generation task + * @description Creates one asynchronous image task. Select the model-specific request + * contract with `model`, save the returned `data.id`, and poll + * `GET /v1/tasks/{task_id}` until the task succeeds or fails. + */ + post: operations["createImageGenerationTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/videos/tasks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create a video generation task + * @description Creates one asynchronous video task. Select the model-specific request + * contract with `model`, save the returned `data.id`, and poll + * `GET /v1/tasks/{task_id}` until the task succeeds or fails. + */ + post: operations["createVideoGenerationTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/effects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List active Effects + * @description Returns only versioned Effects that have passed BeatAPI publication gates. Internal integration names, template ids, costs, and execution context are never exposed. + */ + get: operations["listEffects"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/effects/{effect_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get an active Effect */ + get: operations["getEffect"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/effects/tasks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Create an Effect task + * @description Creates an asynchronous image or video Effect task. Read the catalog + * first: image count, accepted input types, output resolution/duration, + * and execution contract are fixed by the selected Effect version. Send + * an `Idempotency-Key`; an exact replay returns the + * accepted task before remote input URLs are revalidated, while a changed + * body returns `idempotency_conflict`. + * + * The USD amount is reserved atomically when accepted, settled on success, and + * fully refunded after a definite processing failure. An uncertain create + * result is not blindly retried and never switches integrations automatically. + */ + post: operations["createEffectTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/video-analysis/tasks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Analyze an uploaded video + * @description Analyze one MP4 or MOV previously uploaded with `POST /v1/files` by the + * current BeatAPI account. `standard` is the default low-cost route; + * `deep` uses the higher-reasoning route. BeatAPI reserves an estimate from + * verified video duration and the output budget, then settles from actual + * input and output token usage. Standard costs $0.36 per 1M input tokens and + * $1.60 per 1M output tokens; Deep costs $0.72 per 1M input tokens and $5.00 + * per 1M output tokens. Each completed task is rounded up to the nearest + * $0.01 because the shared USD balance settles in cents. Save `data.id` and poll the shared Task + * endpoint if the task remains queued for processing capacity. + */ + post: operations["createVideoAnalysisTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/music-video/tasks": { parameters: { query?: never; @@ -31,24 +289,30 @@ export interface paths { get?: never; put?: never; /** - * Create Music Video - * @description Music Video requires public HTTPS image URLs and a public HTTPS audio URL. - * Prompt, language, quality, style, lip reference, subtitle, and format - * controls are optional. BeatAPI detects the audio duration before task - * creation and charges the detected billable duration at the selected - * per-second customer-credit rate. If audio duration cannot be detected, - * `duration` is used as the billing fallback. + * Create a Music Video workflow task + * @description Music Video requires public HTTPS media URLs. Requests that omit + * `mv_tier` use `standard` and preserve the existing API behavior. + * Premium retains the configured integration host but uses a distinct + * execution path selected internally by BeatAPI. + * Shared controls include prompt, aspect ratio, subtitles, and the tier's + * billing fallback. Language, quality, `lip_sync`, `lip_ref_url`, + * `srt_url`, and `compose_mode` are Standard-only. Premium uses `mv_mode` + * plus `style` and mode-specific images or `lip_ref_urls`. BeatAPI detects audio duration before task + * creation and records the billable duration in Task usage. If audio + * duration cannot be detected, `duration` is used as the billing fallback. * * Input limits: - * - Images must contain 1-7 public HTTPS URLs. + * - Standard images must contain 1-7 public HTTPS URLs. + * - Premium `sing` and `sing_perform` accept 0-6 scene images and require 1-2 `lip_ref_urls`. + * - Premium `dance` and `perform` require exactly 6 scene images. * - Use png, jpg, jpeg, or webp images; each image should be 50 MB or smaller. * - Image aspect ratio should be between 1:4 and 4:1. - * - Audio must be a public HTTPS mp3, wav, aac, or m4a URL between 10 and 180 seconds. + * - Standard audio must be 10-180 seconds; Premium audio must be 10-300 seconds and contain vocals or lyrics rather than instrumental-only audio. * - The audio file should be 50 MB or smaller. * - `prompt` is optional and must be at most 3000 characters. - * - `lip_ref_url`, when provided, must be a public HTTPS image URL. Use a clear, front-facing close-up face reference for best lip-sync results. - * - `srt_url`, when provided, must point to an `.srt` subtitle file. - * - `duration` is only a billing fallback when BeatAPI cannot detect the audio length; it must be 10-180 seconds and cannot override a detected audio duration. + * - Standard `lip_sync=true` requires `lip_ref_url`. It must be a public HTTPS image URL showing a clear, front-facing close-up face. + * - Standard `srt_url`, when provided, must point to an `.srt` subtitle file. + * - `duration` is only a billing fallback when BeatAPI cannot detect the audio length; Standard accepts 10-180 seconds and Premium accepts 10-300 seconds. It cannot override a detected audio duration. * * BeatAPI validates URL shape, text limits, enum values, combination * limits, and audio duration at task creation. Files uploaded through @@ -56,15 +320,6 @@ export interface paths { * Third-party media URLs must follow the same media requirements and may * be rejected during processing if invalid. * - * Customer pricing: - * - MV 540p standard: 4 credits/s - * - MV 720p standard: 5 credits/s - * - MV 1080p standard: 6 credits/s - * - lip_sync add-on: +2 credits/s - * - MV 720p high: 16 credits/s - * - MV 1080p high: 18 credits/s - * - Ecommerce Video 1080p: 15 credits/s - * * Combination limits: * - `quality=high` is not supported with `resolution=540p`. * - `lip_sync=true` is not supported with `resolution=540p`. @@ -93,10 +348,13 @@ export interface paths { get?: never; put?: never; /** - * Edit Shot + * Edit a Music Video storyboard shot * @description Edit one storyboard shot using its BeatAPI `shot_id`. This operation - * charges BeatAPI customer credits using the selected quality/resolution - * rate and the shot duration. Default shot duration is 5 seconds. + * charges the customer USD balance using the applicable task tier and shot + * duration. Standard edits accept only `prompt`. Premium edits accept + * `prompt` plus up to 6 optional replacement `images`. Generation quality, + * resolution, and shot duration are inherited from the original task and + * are not editable request fields. * When the edit finishes, BeatAPI stores the edited shot media and exposes * it on that shot. The existing final Music Video is not replaced until * you call compose with the selected shot ids. @@ -118,7 +376,7 @@ export interface paths { get?: never; put?: never; /** - * Get Shot Media + * Retrieve a Music Video storyboard shot media URL * @description Materialize one storyboard shot video using its BeatAPI `shot_id`. * If the shot has not been stored yet, BeatAPI retrieves the current shot * video, stores it under BeatAPI media storage, and returns a BeatAPI media @@ -144,9 +402,9 @@ export interface paths { get?: never; put?: never; /** - * Compose Video + * Compose a Music Video task from selected shots * @description Compose selected BeatAPI storyboard shots into the final Music Video. - * This operation charges a fixed 1 BeatAPI customer credit. + * This operation charges a fixed $1 USD. */ post: operations["composeMusicVideoTask"]; delete?: never; @@ -165,8 +423,11 @@ export interface paths { get?: never; put?: never; /** - * Create Ecommerce Video - * @description Ecommerce Video requires product images and an explicit output duration. + * Create an Ecommerce Video workflow task + * @description Ecommerce Video creates a complete product video from public HTTPS product or + * lifestyle images, an explicit duration, and optional creative direction. Upload + * local images with `POST /v1/files`, save the returned Task ID, and poll + * `GET /v1/tasks/{task_id}` until the task succeeds or fails. */ post: operations["createEcommerceVideoTask"]; delete?: never; @@ -206,9 +467,9 @@ export interface paths { put?: never; /** * Create a realtime browser session - * @description Reserve credits and allocate a short-lived BeatAPI realtime session. Send a unique + * @description Reserve the selected USD amount and allocate a short-lived BeatAPI realtime session. Send a unique * `Idempotency-Key`; retries with the same user, key, and body return the same session - * and deterministic short-lived `client_secret` without reserving credits or capacity + * and deterministic short-lived `client_secret` without reserving funds or capacity * twice. The browser receives only that BeatAPI secret and connects with * `@beatapi/realtime`. * @@ -279,26 +540,32 @@ export interface paths { put?: never; /** * Upload a file for workflow inputs - * @description Use file upload when your images, audio, or subtitle files are not + * @description Use file upload when your images, videos, audio, or subtitle files are not * already hosted at public HTTPS URLs. The returned HTTPS URL can be used - * directly in `images`, `audio_url`, or `srt_url`. + * directly in `images`, `reference_videos`, `audio_url`, or `srt_url`. * * Limits: - * - Maximum file size is 50 MB. + * - Images, audio, and subtitles: maximum 50 MB. + * - Motion videos: maximum 100 MB. * - Images: `png`, `jpg`, `jpeg`, `webp` * (`image/png`, `image/jpeg`, `image/webp`). * - Audio: `mp3`, `wav`, `aac`, `m4a` * (`audio/mpeg`, `audio/wav`, `audio/aac`, `audio/mp4`). - * - Audio uploads must be 10-180 seconds. + * - Audio uploads must be 10-300 seconds. The selected Music Video tier applies its own task limit: Standard 10-180 seconds; Premium 10-300 seconds. + * - Motion videos: `mp4`, `mov` (`video/mp4`, `video/quicktime`), 3-30 seconds. Duration and dimensions are detected during upload. * - Subtitles: `srt` (`application/x-subrip`; multipart uploads may use * `text/plain` only when the filename ends in `.srt`). - * - PDF, generic text files, octet-stream uploads, videos, and zip files + * - PDF, generic text files, octet-stream uploads, Matroska videos, and zip files * are not supported for launch. * - Send either multipart form-data with a `file` field, or send the raw * file body with the asset `Content-Type`. + * - `Content-Length` is required and is validated before BeatAPI buffers + * the request body; chunked uploads without a declared length are rejected. * - Returned URLs are HTTPS and long-lived for launch. * - Uploaded audio files are duration-checked during upload. The response * includes `audio_duration_seconds` when the uploaded asset is audio. + * - Uploaded videos are signature-, duration-, and dimension-checked. The + * response includes `video_duration_seconds`, `width`, and `height`. * - Workflow task inputs still require public HTTPS URLs. Localhost, * private network URLs, and data URLs are not accepted. */ @@ -321,8 +588,9 @@ export interface paths { put?: never; /** * Create a webhook endpoint - * @description The signing secret is returned only once at creation time. Store it - * securely. Later responses return a masked secret. + * @description The public API returns the signing secret in full at creation time. Store + * it securely; later public API responses return a masked secret. An + * authenticated dashboard owner can explicitly reveal the secret again. * * BeatAPI sends these headers with each delivery: * - `x-beatapi-event`: `task.succeeded` or `task.failed` @@ -386,8 +654,9 @@ export interface paths { * ``` * * Reject old timestamps to prevent replay attacks. A 5 minute window is - * recommended. Failed deliveries are retried at most 3 times with fixed - * backoff windows of 1 minute, 5 minutes, and 15 minutes. Polling + * recommended. A delivery is attempted at most 3 times total: the initial + * request plus up to 2 retries, with fixed backoff windows of 1 minute and + * 5 minutes. Polling * `GET /v1/tasks/{task_id}` remains the source of truth. */ post: operations["createWebhookEndpoint"]; @@ -417,15 +686,64 @@ export interface paths { trace?: never; }; } -export type webhooks = Record; +export interface webhooks { + taskCompleted: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Receive a BeatAPI task completion event + * @description BeatAPI sends this request to each active endpoint subscribed to the event. + * Verify `x-beatapi-signature` against the exact request body and use polling + * as the source of truth if delivery is delayed or fails. + */ + post: operations["receiveBeatApiTaskEvent"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} export interface components { schemas: { + /** @description Public text model id exposed by BeatAPI. Call GET /v1/models to discover the models enabled for your environment. */ + TextModelId: string; + TextModel: { + id: components["schemas"]["TextModelId"]; + /** @constant */ + object: "model"; + /** @example 1788220800 */ + created: number; + /** @constant */ + owned_by: "beatapi"; + }; + TextModelList: { + /** @constant */ + object: "list"; + data: components["schemas"]["TextModel"][]; + }; + /** @description SDK-compatible text request. BeatAPI preserves supported provider-format fields and streams the matching response format back. */ + TextPassthroughRequest: { + model: components["schemas"]["TextModelId"]; + } & { + [key: string]: unknown; + }; + /** @description Response body in the selected SDK-compatible wire format. */ + TextPassthroughResponse: { + [key: string]: unknown; + }; Workflow: { /** * @example music-video * @enum {string} */ - id: "music-video" | "ecommerce-video"; + id: "music-video" | "ecommerce-video" | "video-analysis"; /** @enum {string} */ object: "workflow"; /** @example Music Video API */ @@ -440,33 +758,60 @@ export interface components { * @example shot_xxx */ id: string; - /** @example 0 */ + /** + * @description Zero-based shot order in the storyboard. + * @example 0 + */ index: number; + /** @description Current lifecycle state for this storyboard shot. */ status: components["schemas"]["TaskStatus"]; - /** @example 5 */ + /** + * @description Planned or generated shot duration in seconds. + * @example 5 + */ duration_seconds?: number; - /** @example Opening lyric shot. */ + /** + * @description Creative instruction used to generate this shot. + * @example Opening lyric shot. + */ prompt?: string; - /** @example Intro */ + /** + * @description Lyric segment aligned with this shot when available. + * @example Intro + */ lyric_text?: string; /** @description Present only after the shot media has been materialized or after a shot edit finishes. */ media?: { - /** @example video */ + /** + * @description Hosted media type for the materialized shot. + * @example video + */ type?: string; /** * Format: uri + * @description BeatAPI-hosted HTTPS URL for the materialized shot. * @example https://media.beatapi.io/outputs/task_8K2qA/shots/0.mp4 */ url?: string; - /** @example video/mp4 */ + /** + * @description MIME type of the hosted shot media. + * @example video/mp4 + */ mime_type?: string; }; - /** @example 1782210000 */ + /** + * @description Unix timestamp when the shot record was created. + * @example 1782210000 + */ created_at: number; - /** @example 1782210300 */ + /** + * @description Unix timestamp when the shot record last changed. + * @example 1782210300 + */ updated_at: number; }; Storyboard: { + /** @description Ordered Music Video storyboard shots. The array may be empty before storyboard generation completes. */ shots: components["schemas"]["StoryboardShot"][]; }; ShotMedia: { @@ -498,68 +843,223 @@ export interface components { request_id: string; }; TaskUsage: { - /** @description BeatAPI customer credits reserved for this task. */ + /** + * Format: double + * @description USD amount reserved for this task. The compatibility field name is retained; 1 Credit equals $1 USD. + */ credits_reserved: number; - /** @description BeatAPI customer credits charged when the task or operation is accepted. */ + /** + * Format: double + * @description USD amount charged when the task or operation is accepted. + */ credits_charged: number; - /** @description Server-detected or request-declared billable duration used for credit calculation. */ + /** @description Server-detected or request-declared billable duration used for USD calculation. */ billable_duration_seconds?: number; - /** @description BeatAPI customer credits settled after successful work. */ + /** + * Format: double + * @description USD amount settled after successful work. + */ credits_settled: number; - /** @description BeatAPI customer credits refunded after failed eligible work. */ + /** + * Format: double + * @description USD amount refunded after failed eligible work. + */ credits_refunded: number; }; Task: { - /** @example task_8K2qA */ + /** + * @description Stable BeatAPI task ID used for polling and support. + * @example task_8K2qA + */ id: string; - /** @enum {string} */ + /** + * @description Object discriminator; always `task`. + * @enum {string} + */ object: "task"; /** + * @description Public task family that determines which capability fields are present. + * @enum {string} + */ + task_kind: "workflow" | "effect" | "image" | "video"; + /** @description Stable BeatAPI workflow, Effect, or generation model ID selected when the task was accepted. */ + capability_id: string; + /** @description Immutable capability version used by this task. Legacy workflow rows are returned as version 1. */ + capability_version: number | null; + /** + * @description Present for workflow tasks; identifies the selected BeatAPI workflow. * @example music-video * @enum {string} */ - workflow: "music-video" | "ecommerce-video"; - status: components["schemas"]["TaskStatus"]; - stage: components["schemas"]["TaskStatus"]; - storyboard?: components["schemas"]["Storyboard"]; - created_at: number; + workflow?: "music-video" | "ecommerce-video" | "video-analysis"; + /** + * @description Present for Effect tasks; stable selected Effect ID. + * @example video-muscle-max + */ + effect_id?: string; + /** + * @description Present for Effect tasks; immutable Effect version used for processing. + * @example 1 + */ + effect_version?: number; + /** + * @description Present when task_kind is image or video. + * @enum {string} + */ + media_type?: "image" | "video"; + /** @description Stable BeatAPI model alias. It is independent from internal execution routing. */ + model?: string; + /** @description Current task lifecycle status. Stop polling at `succeeded` or `failed`; Music Video can also require manual action. */ + status: components["schemas"]["TaskStatus"]; + /** @description Current processing stage, exposed separately so workflow progress can be tracked. */ + stage: components["schemas"]["TaskStatus"]; + /** @description Music Video storyboard metadata when available. */ + storyboard?: components["schemas"]["Storyboard"]; + /** @description Unix timestamp when BeatAPI accepted the task. */ + created_at: number; + /** @description Unix timestamp of the latest task update. */ updated_at: number; + /** @description Terminal Unix timestamp, or null while work is in progress. */ completed_at: number | null; /** @description Output is null until the task succeeds. */ output: null | { + /** @description BeatAPI-hosted result assets. */ media: { - /** @enum {string} */ - type: "video"; - /** Format: uri */ + /** + * @description Result asset type. + * @enum {string} + */ + type: "image" | "video"; + /** + * Format: uri + * @description BeatAPI-hosted HTTPS result URL. + */ url: string; - /** @example video/mp4 */ + /** + * @description Result asset MIME type. + * @example video/mp4 + * @example image/png + * @example image/jpeg + * @example image/webp + */ mime_type: string; }[]; - /** Format: uri */ + /** + * Format: uri + * @description Primary BeatAPI-hosted result URL for clients that need one canonical asset. + */ r2_url: string; + } | { + /** @description Completed video analysis text. */ + text: string; + /** @description Measured token usage used for final USD settlement. */ + usage: { + /** @description Tokens consumed by the prompt and video input. */ + input_tokens: number; + /** @description Tokens consumed by visible output and model reasoning. */ + output_tokens: number; + /** @description Total measured input and output tokens. */ + total_tokens: number; + }; + /** @description Upstream-compatible completion reason. */ + finish_reason: string | null; }; + /** @description USD reservation, settlement, refund, and optional billable duration for this task. */ usage: components["schemas"]["TaskUsage"]; - /** @example req_abc123 */ + /** + * @description Correlation ID to retain for logs and BeatAPI support. + * @example req_abc123 + */ request_id: string; - /** @example processing_timeout */ + /** + * @description Machine-readable terminal failure code, or null when no task failure is recorded. + * @example processing_timeout + */ error_code: string | null; + /** @description Human-readable terminal failure detail, or null when no task failure is recorded. */ error_message: string | null; }; - File: { - /** @example file_3xYz9 */ + Effect: { + /** @example video-muscle-max */ id: string; /** @enum {string} */ + object: "effect"; + /** @example Muscle Transformation */ + name: string; + description: string; + /** @enum {string} */ + output_type: "image" | "video"; + /** @example transformation */ + category: string; + tags: string[]; + input: { + images_min: number; + images_max: number; + accepted_types: ("image/jpeg" | "image/png" | "image/webp")[]; + /** @description Maximum downloaded bytes per input image. When omitted, BeatAPI enforces 50 MB. */ + max_size_mb?: number; + /** @description Maximum decoded width or height. BeatAPI inspects the actual image header before charging. */ + max_dimension_px?: number; + subject_requirements?: string[]; + }; + options: { + aspect_ratios?: string[]; + resolutions?: string[]; + duration_seconds?: number[]; + bgm?: boolean; + seed?: boolean; + }; + preview: { + /** Format: uri */ + cover_url: string | null; + /** Format: uri */ + media_url: string | null; + }; + version: number; + /** @enum {string} */ + status: "testing" | "active" | "paused"; + }; + EffectResponse: { + data: components["schemas"]["Effect"]; + }; + EffectListResponse: { + data: { + /** @enum {string} */ + object: "list"; + data: components["schemas"]["Effect"][]; + }; + }; + File: { + /** + * @description Stable uploaded file ID. + * @example file_3xYz9 + */ + id: string; + /** + * @description Object discriminator; always `file`. + * @enum {string} + */ object: "file"; /** * Format: uri + * @description Long-lived BeatAPI HTTPS URL to use in workflow or model requests. * @example https://media.beatapi.io/inputs/file_3xYz9.mp3 */ url: string; - /** @example inputs/file_3xYz9.mp3 */ + /** + * @description BeatAPI storage key for support and diagnostics. + * @example inputs/file_3xYz9.mp3 + */ key: string; - /** @example audio/mpeg */ + /** + * @description Accepted MIME type detected for the uploaded file. + * @example audio/mpeg + */ mime_type: string; - /** @example 1048576 */ + /** + * @description Uploaded file size in bytes. + * @example 1048576 + */ size_bytes: number; /** * @description Present for uploaded audio files after server-side duration detection. @@ -571,32 +1071,74 @@ export interface components { * @example mp3_frame_scan */ audio_duration_source?: string; - /** @enum {string} */ + /** + * @description Present for MP4/MOV uploads after server-side container inspection. + * @example 15.25 + */ + video_duration_seconds?: number; + /** + * @description Duration and dimension detection method used for the uploaded video. + * @example mp4_boxes + */ + video_duration_source?: string; + /** + * @description Detected pixel width for uploaded images and videos. + * @example 720 + */ + width?: number; + /** + * @description Detected pixel height for uploaded images and videos. + * @example 1280 + */ + height?: number; + /** + * @description File purpose; currently always `input`. + * @enum {string} + */ purpose: "input"; - /** @example 1782210000 */ + /** + * @description Unix timestamp when the file was stored. + * @example 1782210000 + */ created_at: number; }; WebhookEndpoint: { - /** @example wh_9aBcD */ + /** + * @description Stable webhook endpoint ID used for get, update, and delete operations. + * @example wh_9aBcD + */ id: string; - /** @enum {string} */ + /** + * @description Object discriminator; always `webhook_endpoint`. + * @enum {string} + */ object: "webhook_endpoint"; /** * Format: uri + * @description Public HTTPS callback URL receiving subscribed task events. * @example https://example.com/beatapi-webhook */ url: string; - /** @example Production webhook */ + /** + * @description Account-defined label for the endpoint. + * @example Production webhook + */ description: string; + /** @description Task event types delivered to this endpoint. */ events: ("task.succeeded" | "task.failed")[]; - /** @enum {string} */ + /** + * @description Delivery status. Disabled endpoints do not receive events. + * @enum {string} + */ status: "active" | "disabled"; /** * @description Returned in full only when the endpoint is created. Later responses return a masked value. * @example whsec_example_masked */ secret: string; + /** @description Unix timestamp when the endpoint was created. */ created_at: number; + /** @description Unix timestamp when the endpoint last changed. */ updated_at: number; }; WebhookEvent: { @@ -615,128 +1157,1237 @@ export interface components { WorkflowListResponse: { data: components["schemas"]["WorkflowList"]; }; - TaskResponse: { - data: components["schemas"]["Task"]; + GenerationModel: { + /** @enum {string} */ + id: "nano-banana" | "nano-banana-2" | "nano-banana-2-lite" | "nano-banana-pro" | "gpt-image-2" | "seedream-5-pro" | "grok-imagine-image-2.0" | "minimax-h3" | "grok-imagine-video-1.5" | "seedance-2" | "seedance-2-fast" | "seedance-2-mini" | "veo-3.1" | "seedance-2.5" | "kling-3" | "kling-2.6-motion-control" | "kling-3-motion-control" | "wan-3.0" | "wan-3.0-prime" | "happyhorse-1.0" | "happyhorse-1.1" | "minimax-h3-max" | "minimax-h3-max-turbo"; + /** @enum {string} */ + object: "generation_model"; + name: string; + /** @enum {string} */ + media_type: "image" | "video"; + input_modes: ("text" | "image" | "frames" | "reference")[]; }; - Usage: { + GenerationModelList: { /** @enum {string} */ - object: "usage"; - /** @description Current credit balance. It may be negative. */ - credit_balance: number; - total_tasks: number; - credits_settled: number; - credits_refunded: number; - concurrency: { - /** @example 2 */ - limit: number; - /** @description Active processing tasks currently using BeatAPI processing resources. Music Video storyboard_ready and requires_action tasks can have settled credits without counting toward this value. */ - active: number; - }; - by_workflow: { - /** @enum {string} */ - workflow: "music-video" | "ecommerce-video"; - tasks: number; - credits_settled: number; - }[]; - realtime?: { - /** @description Total BeatAPI realtime sessions for this account. */ - sessions: number; - /** @description Credits settled by connected realtime sessions. */ - credits: number; - /** @description Realtime sessions in ready, connecting, or active state. */ - active: number; - }; + object: "list"; + data: components["schemas"]["GenerationModel"][]; }; - UsageResponse: { - data: components["schemas"]["Usage"]; + GenerationModelListResponse: { + data: components["schemas"]["GenerationModelList"]; }; - RealtimeSession: { - id: string; - /** @enum {string} */ - object: "realtime.session"; + ImageGenerationTaskCreateRequest: components["schemas"]["NanoBananaImageRequest"] | components["schemas"]["NanoBanana2ImageRequest"] | components["schemas"]["NanoBanana2LiteImageRequest"] | components["schemas"]["NanoBananaProImageRequest"] | components["schemas"]["GptImage2Request"] | components["schemas"]["Seedream5ProImageRequest"] | components["schemas"]["GrokImagineImage20Request"]; + NanoBananaImageRequest: { /** - * @description Active means BeatAPI accepted the first billing heartbeat after remote output began. + * @description Must be `nano-banana`. (enum property replaced by openapi-typescript) * @enum {string} */ - status: "ready" | "connecting" | "active" | "closed" | "failed" | "expired"; - /** @description Returned only by POST. Give this short-lived BeatAPI secret to the browser SDK; never give the browser an sk_ API key. */ - client_secret?: string; - /** Format: date-time */ - expires_at: string; - /** @enum {integer} */ - max_duration_seconds: 15 | 60 | 300; - allowed_origins: string[]; - credits: { - reserved: number; - settled: number; - refunded: number; - }; - request_id: string; - /** Format: date-time */ - created_at: string; + model: "nano-banana"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; /** - * Format: date-time - * @description Time of the first accepted BeatAPI billing heartbeat; null before billing activation. + * @description Output image aspect ratio. + * @default 1:1 + * @enum {string} */ - connected_at: string | null; - /** Format: date-time */ - closed_at: string | null; + aspect_ratio: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto"; + /** + * @description Output image file format. + * @default png + * @enum {string} + */ + output_format: "png" | "jpeg"; }; - RealtimeSessionResponse: { - data: components["schemas"]["RealtimeSession"]; + NanoBanana2ImageRequest: { + /** + * @description Must be `nano-banana-2`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "nano-banana-2"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. + * @default 1:1 + * @enum {string} + */ + aspect_ratio: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto"; + /** + * @description Output resolution tier. + * @default 1K + * @enum {string} + */ + resolution: "1K" | "2K" | "4K"; + /** + * @description Output image file format. + * @default png + * @enum {string} + */ + output_format: "png" | "jpeg"; }; - FileResponse: { - data: components["schemas"]["File"]; + NanoBanana2LiteImageRequest: { + /** + * @description Must be `nano-banana-2-lite`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "nano-banana-2-lite"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. + * @default 1:1 + * @enum {string} + */ + aspect_ratio: "1:1" | "9:16" | "16:9" | "3:4" | "4:3" | "3:2" | "2:3" | "5:4" | "4:5" | "21:9" | "auto"; + /** + * @description Output image file format. + * @default png + * @enum {string} + */ + output_format: "png" | "jpeg"; }; - WebhookEndpointList: { - /** @enum {string} */ - object: "list"; - data: components["schemas"]["WebhookEndpoint"][]; + NanoBananaProImageRequest: { + /** + * @description Must be `nano-banana-pro`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "nano-banana-pro"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. + * @default 1:1 + * @enum {string} + */ + aspect_ratio: "1:1" | "2:3" | "3:2" | "3:4" | "4:3" | "4:5" | "5:4" | "9:16" | "16:9" | "21:9" | "auto"; + /** + * @description Output resolution tier. + * @default 1K + * @enum {string} + */ + resolution: "1K" | "2K" | "4K"; + /** + * @description Output image file format. + * @default png + * @enum {string} + */ + output_format: "png" | "jpg"; }; - WebhookEndpointListResponse: { - data: components["schemas"]["WebhookEndpointList"]; + GptImage2Request: { + /** + * @description Must be `gpt-image-2`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "gpt-image-2"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. + * @default auto + * @enum {string} + */ + aspect_ratio: "auto" | "1:1" | "3:2" | "2:3" | "4:3" | "3:4" | "5:4" | "4:5" | "16:9" | "9:16" | "2:1" | "1:2" | "3:1" | "1:3" | "21:9" | "9:21"; + /** + * @description Output resolution tier. + * @default 1K + * @enum {string} + */ + resolution: "1K" | "2K" | "4K"; }; - WebhookEndpointResponse: { - data: components["schemas"]["WebhookEndpoint"]; + Seedream5ProImageRequest: { + /** + * @description Must be `seedream-5-pro`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "seedream-5-pro"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. + * @default 1:1 + * @enum {string} + */ + aspect_ratio: "auto" | "1:1" | "4:3" | "3:4" | "16:9" | "9:16" | "3:2" | "2:3" | "21:9"; + /** + * @description Output resolution tier. + * @default 1K + * @enum {string} + */ + resolution: "1K" | "2K" | "4K"; + /** + * @description Output image file format. + * @default png + * @enum {string} + */ + output_format: "png" | "jpeg"; }; - DeleteResponse: { - data: { - id: string; - deleted: boolean; - }; + /** @description Omit `images` for text-to-image. Supply one to five images for editing; `auto` aspect ratio is available only when images are supplied. */ + GrokImagineImage20Request: { + /** + * @description Must be `grok-imagine-image-2.0`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "grok-imagine-image-2.0"; + /** @description Generation or image-editing instructions. */ + prompt: string; + /** @description Public HTTPS reference-image URLs. Omit for text-to-image. */ + images?: string[]; + /** + * @description Output image aspect ratio. `auto` requires at least one image. + * @default 1:1 + * @enum {string} + */ + aspect_ratio: "1:1" | "2:3" | "3:2" | "16:9" | "9:16" | "auto"; }; - Error: { - error: { - /** @enum {string} */ - code: "bad_request" | "unauthorized" | "forbidden" | "not_found" | "insufficient_credits" | "idempotency_conflict" | "user_concurrency_exceeded" | "rate_limit_exceeded" | "processing_unavailable" | "processing_failed" | "processing_timeout" | "result_transfer_failed" | "invalid_signature" | "realtime_disabled" | "realtime_capacity_unavailable" | "realtime_session_expired" | "origin_not_allowed" | "invalid_client_secret" | "transport_not_allowed" | "internal_error"; - message: string; - request_id: string; - /** @description Present on retryable rate-limit or capacity responses when the client should wait before retrying. */ - retry_after_seconds?: number; - }; + VideoGenerationTaskCreateRequest: components["schemas"]["MinimaxH3VideoRequest"] | components["schemas"]["GrokImagineVideo15Request"] | components["schemas"]["Seedance2VideoRequest"] | components["schemas"]["Seedance2FastVideoRequest"] | components["schemas"]["Seedance2MiniVideoRequest"] | components["schemas"]["Veo31VideoRequest"] | components["schemas"]["Seedance25VideoRequest"] | components["schemas"]["Kling3VideoRequest"] | components["schemas"]["Kling26MotionControlVideoRequest"] | components["schemas"]["Kling3MotionControlVideoRequest"] | components["schemas"]["Wan30VideoRequest"] | components["schemas"]["Wan30PrimeVideoRequest"] | components["schemas"]["HappyHorse10VideoRequest"] | components["schemas"]["HappyHorse11VideoRequest"] | components["schemas"]["MinimaxH3MaxVideoRequest"] | components["schemas"]["MinimaxH3MaxTurboVideoRequest"]; + /** @description `images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video. */ + MinimaxH3VideoRequest: { + /** + * @description Must be `minimax-h3`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "minimax-h3"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** @description Public HTTPS image references for multimodal reference generation. */ + reference_images?: string[]; + /** @description Public HTTPS video references for multimodal reference generation. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references for multimodal reference generation. Audio also requires at least one reference image or video. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Text mode defaults to 16:9 and does not accept adaptive. Frame mode always uses adaptive. Reference mode defaults to adaptive and also accepts a concrete ratio. + * @enum {string} + */ + aspect_ratio?: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16"; + /** + * @description Output resolution tier. + * @default 768P + * @enum {string} + */ + resolution: "768P" | "2K"; }; - }; - responses: { - /** @description Missing, invalid, or inactive API key. */ - Unauthorized: { - headers: { - [name: string]: unknown; - }; - content: { - /** - * @example { - * "error": { - * "code": "unauthorized", - * "message": "Missing or invalid API key.", - * "request_id": "req_xxx" - * } - * } - */ - "application/json": components["schemas"]["Error"]; + /** @description `images` accepts one first frame and cannot be combined with `reference_images`. Omit `aspect_ratio` when `images` is supplied. 1080p accepts at most one image. */ + GrokImagineVideo15Request: { + /** + * @description Must be `grok-imagine-video-1.5`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "grok-imagine-video-1.5"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image as a public HTTPS URL. */ + images?: string[]; + /** @description One to seven public HTTPS reference images. */ + reference_images?: string[]; + /** + * @description Requested output duration in seconds. + * @default 8 + */ + duration: number; + /** + * @description Output video aspect ratio. Omit when one first-frame image is supplied. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "1:1" | "16:9" | "9:16" | "3:2" | "2:3" | "auto"; + /** + * @description Output resolution tier. 1080p accepts at most one image. + * @default 480p + * @enum {string} + */ + resolution: "480p" | "720p" | "1080p"; + }; + /** @description `images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video. */ + Seedance2VideoRequest: { + /** + * @description Must be `seedance-2`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "seedance-2"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** @description Public HTTPS image references for multimodal reference generation. */ + reference_images?: string[]; + /** @description Public HTTPS video references for multimodal reference generation. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. Audio also requires at least one reference image or video. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default adaptive + * @enum {string} + */ + aspect_ratio: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16"; + /** + * @description Output resolution tier. 4k and 4K are equivalent. 1080p is not supported with reference images. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p" | "1080p" | "4k" | "4K"; + /** + * @description Generate synchronized audio with the video. + * @default true + */ + generate_audio: boolean; + }; + /** @description `images` cannot be combined with any `reference_*` input. An audio reference also requires at least one reference image or video. */ + Seedance2FastVideoRequest: { + /** + * @description Must be `seedance-2-fast`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "seedance-2-fast"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** @description Public HTTPS image references for multimodal reference generation. */ + reference_images?: string[]; + /** @description Public HTTPS video references for multimodal reference generation. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. Audio also requires at least one reference image or video. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default adaptive + * @enum {string} + */ + aspect_ratio: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16"; + /** + * @description Output resolution tier. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p"; + /** + * @description Generate synchronized audio with the video. + * @default true + */ + generate_audio: boolean; + }; + /** @description Low-cost Seedance 2.0 route. `images` cannot be combined with any `reference_*` input. Generated audio is not supported. */ + Seedance2MiniVideoRequest: { + /** + * @description Must be `seedance-2-mini`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "seedance-2-mini"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** @description Public HTTPS image references for multimodal reference generation. */ + reference_images?: string[]; + /** @description Public HTTPS video references for multimodal reference generation. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. Audio also requires at least one reference image or video. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default adaptive + * @enum {string} + */ + aspect_ratio: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16"; + /** + * @description Output resolution tier. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p"; + }; + Veo31VideoRequest: (components["schemas"]["Veo31TextOrFrameVideoRequest"] | components["schemas"]["Veo31ReferenceVideoRequest"]) & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + model: "veo-3.1"; + }; + /** + * @description Veo 3.1 text or first/last-frame generation. Output is fixed at 8 seconds + * and defaults to Quality at 720p. Price depends on quality and resolution. + */ + Veo31TextOrFrameVideoRequest: { + /** + * @description Must be `veo-3.1`. + * @constant + */ + model: "veo-3.1"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "auto"; + /** + * @description Output video resolution. 4k and 4K are equivalent. Price depends on quality and resolution. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p" | "4k" | "4K"; + /** + * @description Text or frame generation tier. + * @default Quality + * @enum {string} + */ + quality: "Quality" | "Fast" | "Lite"; + /** @description Optional watermark text forwarded to the selected model. */ + watermark?: string; + /** @description Allow prompt translation before generation. */ + enable_translation?: boolean; + }; + /** + * @description Veo 3.1 reference-image generation. Output is fixed at 8 seconds and + * supports the Fast or Lite tier, defaulting to Fast at 720p. Price depends + * on quality and resolution. + */ + Veo31ReferenceVideoRequest: { + /** + * @description Must be `veo-3.1`. + * @constant + */ + model: "veo-3.1"; + /** @description Video generation instructions. */ + prompt: string; + /** @description Public HTTPS reference images. */ + reference_images: string[]; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "auto"; + /** + * @description Output video resolution. 4k and 4K are equivalent. Price depends on quality and resolution. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p" | "4k" | "4K"; + /** + * @description Reference-image generation tier. + * @default Fast + * @enum {string} + */ + quality: "Fast" | "Lite"; + /** @description Optional watermark text forwarded to the selected model. */ + watermark?: string; + /** @description Allow prompt translation before generation. */ + enable_translation?: boolean; + }; + /** @description `images` cannot be combined with any `reference_*` input. Audio-only reference generation is supported. */ + Seedance25VideoRequest: { + /** + * @description Must be `seedance-2.5`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "seedance-2.5"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. */ + images?: string[]; + /** @description Public HTTPS image references for multimodal reference generation. */ + reference_images?: string[]; + /** @description Public HTTPS video references for multimodal reference generation. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. Audio-only reference generation is supported. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default adaptive + * @enum {string} + */ + aspect_ratio: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16"; + /** + * @description Output resolution tier. 480p and 1080p are priced separately; see the pricing page. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p" | "1080p"; + /** + * @description Generate synchronized audio with the video. + * @default true + */ + generate_audio: boolean; + /** + * @description Reproducibility seed. Use -1 for a random seed. + * @default -1 + */ + seed: number; + }; + KlingShot: { + /** @description Instructions for this shot. */ + prompt: string; + /** @description Shot duration in seconds. All shot durations must sum to the task duration. */ + duration: number; + }; + /** @description Use 2-4 image URLs or one video URL. A video element may include one audio URL and a 3-8 second segment in milliseconds. */ + KlingElement: { + /** @description Stable name used to reference this element in the prompt. */ + name: string; + /** @description Optional description of the subject or object. */ + description?: string; + /** @description Two to four image URLs, or one video URL. */ + element_input_urls: string[]; + /** @description Optional audio URL used with a video element. */ + element_input_audio_urls?: string[]; + /** @description Video element segment start time in milliseconds. */ + start_time?: number; + /** @description Video element segment end time in milliseconds. The segment must be 3-8 seconds. */ + end_time?: number; + }; + /** @description Multi-shot mode accepts one first-frame image, requires `multi_prompt`, and defaults sound to true. Shot durations must sum to `duration`. */ + Kling3VideoRequest: { + /** + * @description Must be `kling-3`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "kling-3"; + /** @description Video generation instructions. */ + prompt: string; + /** @description One first-frame image or first- and last-frame images as public HTTPS URLs. Multi-shot mode accepts exactly one. */ + images?: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Defaults to 16:9 for text generation. Omit it with frame images to adapt to the input aspect ratio. + * @enum {string} + */ + aspect_ratio?: "16:9" | "9:16" | "1:1"; + /** + * @description Output quality tier. + * @default pro + * @enum {string} + */ + resolution: "std" | "pro" | "4K"; + /** @description Generate synchronized sound. Defaults to true in multi-shot mode. */ + sound?: boolean; + /** + * @description Enable storyboard-style multi-shot generation. + * @default false + */ + multi_shots: boolean; + /** @description Shot definitions required when `multi_shots=true`. */ + multi_prompt?: components["schemas"]["KlingShot"][]; + /** @description Up to three reusable subject or object references. */ + elements?: components["schemas"]["KlingElement"][]; + }; + /** + * @description Transfer motion from one uploaded 3–30 second MP4/MOV video to one + * uploaded character image. Both URLs must come from `/v1/files` for the + * current BeatAPI account. BeatAPI detects the reference-video duration + * server-side and reserves USD at the selected per-second rate, rounding + * fractional seconds up. The image must be 10 MB or smaller; the video + * may be up to 100 MB. + */ + Kling26MotionControlVideoRequest: { + /** + * @description Must be `kling-2.6-motion-control`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "kling-2.6-motion-control"; + /** @description Optional motion or scene guidance. */ + prompt?: string; + /** @description Exactly one character-image URL returned by the current account's `/v1/files` upload. */ + images: string[]; + /** @description Exactly one 3–30 second MP4/MOV URL returned by the current account's `/v1/files` upload. Its detected duration determines billing. */ + reference_videos: string[]; + /** + * @description Output resolution and per-second price tier. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p"; + /** + * @description Image orientation supports motion videos up to 10 seconds; video orientation supports up to 30 seconds. + * @default image + * @enum {string} + */ + character_orientation: "image" | "video"; + }; + /** + * @description Kling 3.0 motion transfer using exactly one uploaded image and one + * uploaded 3–30 second MP4/MOV. Both assets must exceed 340 px in width + * and height and use an aspect ratio from 2:5 to 5:2. BeatAPI detects the + * reference-video duration server-side and reserves USD at the selected + * per-second rate, rounding fractional seconds up. + */ + Kling3MotionControlVideoRequest: { + /** + * @description Must be `kling-3-motion-control`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "kling-3-motion-control"; + /** @description Optional motion or scene guidance. */ + prompt?: string; + /** @description Exactly one character-image URL returned by the current account's `/v1/files` upload; maximum 10 MB. */ + images: string[]; + /** @description Exactly one 3–30 second MP4/MOV URL returned by the current account's `/v1/files` upload; maximum 100 MB. Its detected duration determines billing. */ + reference_videos: string[]; + /** + * @description Output resolution and per-second price tier. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p"; + /** + * @description Image orientation supports motion videos up to 10 seconds; video orientation supports up to 30 seconds. + * @default image + * @enum {string} + */ + character_orientation: "image" | "video"; + /** + * @description Preserve the background from the motion video or character image. + * @default input_video + * @enum {string} + */ + background_source: "input_video" | "input_image"; + }; + /** @description Renders 2–30 seconds in a single pass. `images` starts the render from a picture; reference videos and audio travel alongside it. */ + Wan30VideoRequest: { + /** + * @description Must be `wan-3.0`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "wan-3.0"; + /** @description Video generation instructions. */ + prompt: string; + /** @description Public HTTPS images. One starts an image-to-video render; more are used as visual references. */ + images?: string[]; + /** @description Public HTTPS video references. A request that carries one is billed at 1.5x. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. Any whole number in range; there is no long-clip surcharge. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "1:1" | "4:3" | "3:4"; + /** + * @description Output resolution tier. Price scales with it. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p" | "1080p"; + }; + /** @description Renders 2–30 seconds in a single pass. `images` starts the render from a picture; reference videos and audio travel alongside it. */ + Wan30PrimeVideoRequest: { + /** + * @description Must be `wan-3.0-prime`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "wan-3.0-prime"; + /** @description Video generation instructions. */ + prompt: string; + /** @description Public HTTPS images. One starts an image-to-video render; more are used as visual references. */ + images?: string[]; + /** @description Public HTTPS video references. A request that carries one is billed at 1.5x. */ + reference_videos?: string[]; + /** @description Public HTTPS audio references. */ + reference_audios?: string[]; + /** + * @description Requested output duration in seconds. Any whole number in range; there is no long-clip surcharge. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "1:1" | "4:3" | "3:4"; + /** + * @description Output resolution tier. Price scales with it. + * @default 720p + * @enum {string} + */ + resolution: "480p" | "720p" | "1080p"; + }; + /** @description Image to video only — this model publishes no text-to-video mode, so `images` is required. */ + HappyHorse10VideoRequest: { + /** + * @description Must be `happyhorse-1.0`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "happyhorse-1.0"; + /** @description What should happen in the shot — the motion, the expression, the camera. */ + prompt: string; + /** @description Source images as public HTTPS URLs. At least one is required. */ + images: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "1:1" | "4:3" | "3:4"; + /** + * @description Output resolution tier. Price scales with it. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p"; + }; + /** @description Image to video only — this model publishes no text-to-video mode, so `images` is required. */ + HappyHorse11VideoRequest: { + /** + * @description Must be `happyhorse-1.1`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "happyhorse-1.1"; + /** @description What should happen in the shot — the motion, the expression, the camera. */ + prompt: string; + /** @description Source images as public HTTPS URLs. At least one is required. */ + images: string[]; + /** + * @description Requested output duration in seconds. + * @default 5 + */ + duration: number; + /** + * @description Output video aspect ratio. + * @default 16:9 + * @enum {string} + */ + aspect_ratio: "16:9" | "9:16" | "1:1" | "4:3" | "3:4"; + /** + * @description Output resolution tier. Price scales with it. + * @default 720p + * @enum {string} + */ + resolution: "720p" | "1080p"; + }; + /** @description Text to video, or image to video when `images` carries a first frame. A second image becomes the last frame. Output tops out at 768P — MiniMax H3 renders 2K for less per second. */ + MinimaxH3MaxVideoRequest: { + /** + * @description Must be `minimax-h3-max`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "minimax-h3-max"; + /** @description What should happen in the shot — the motion, the expression, the camera. */ + prompt: string; + /** @description Public HTTPS images. One starts the render from a first frame; a second becomes the last frame, in first-to-last order. */ + images?: string[]; + /** + * @description Requested output duration in seconds. Billed per second at the rate for the chosen resolution. If a duration is unsupported, the API returns `400`. + * @default 5 + */ + duration: number; + /** + * @description Output resolution. Upper-case P, and the only two values this model accepts. Price scales with it. + * @default 768P + * @enum {string} + */ + resolution: "480P" | "768P"; + /** @description Reuse a seed to re-render the same motion. A random seed is chosen when omitted. */ + seed?: number; + }; + /** @description H3 Max on a faster stack — the same request contract and the same modes, roughly 2.5x quicker, at half the per-second rate. */ + MinimaxH3MaxTurboVideoRequest: { + /** + * @description Must be `minimax-h3-max-turbo`. (enum property replaced by openapi-typescript) + * @enum {string} + */ + model: "minimax-h3-max-turbo"; + /** @description What should happen in the shot — the motion, the expression, the camera. */ + prompt: string; + /** @description Public HTTPS images. One starts the render from a first frame; a second becomes the last frame, in first-to-last order. */ + images?: string[]; + /** + * @description Requested output duration in seconds. Billed per second at the rate for the chosen resolution. If a duration is unsupported, the API returns `400`. + * @default 5 + */ + duration: number; + /** + * @description Output resolution. Upper-case P, and the only two values this model accepts. Price scales with it. + * @default 768P + * @enum {string} + */ + resolution: "480P" | "768P"; + /** @description Reuse a seed to re-render the same motion. A random seed is chosen when omitted. */ + seed?: number; + }; + TaskResponse: { + /** @description Accepted or current BeatAPI task state. */ + data: components["schemas"]["Task"]; + }; + Usage: { + /** @enum {string} */ + object: "usage"; + /** + * Format: double + * @description Current USD balance. The compatibility field name is retained; 1 Credit equals $1 USD. The balance may be negative. + */ + credit_balance: number; + total_tasks: number; + /** Format: double */ + credits_settled: number; + /** Format: double */ + credits_refunded: number; + concurrency: { + /** @example 2 */ + limit: number; + /** @description Active processing tasks currently using BeatAPI processing resources. Music Video storyboard_ready and requires_action tasks can have settled USD usage without counting toward this value. */ + active: number; + }; + /** @description Compatibility view containing workflow tasks only. Image, video, and Effect tasks are reported under by_capability instead. */ + by_workflow: { + /** @enum {string} */ + workflow: "music-video" | "ecommerce-video" | "video-analysis"; + tasks: number; + /** Format: double */ + credits_settled: number; + }[]; + by_capability: { + /** @enum {string} */ + task_kind: "workflow" | "effect" | "image" | "video"; + capability_id: string; + tasks: number; + /** Format: double */ + credits_settled: number; + }[]; + by_model: { + /** @enum {string} */ + media_type: "image" | "video"; + model: string; + tasks: number; + /** Format: double */ + credits_settled: number; + }[]; + by_api_key: { + api_key_id: string; + title: string; + key_prefix: string; + tasks: number; + /** Format: double */ + credits_settled: number; + }[]; + realtime?: { + /** @description Total BeatAPI realtime sessions for this account. */ + sessions: number; + /** + * Format: double + * @description USD amount settled by connected realtime sessions. + */ + credits: number; + /** @description Realtime sessions in ready, connecting, or active state. */ + active: number; + }; + }; + UsageResponse: { + data: components["schemas"]["Usage"]; + }; + VideoAnalysisTaskCreateRequest: { + /** + * Format: uri + * @description BeatAPI-hosted MP4 or MOV input URL returned by POST /v1/files for the current account. Maximum verified duration is 600 seconds. + */ + video_url: string; + /** @description Analysis instruction. Ask for timestamped output when temporal precision matters. */ + prompt: string; + /** + * @description Standard is the default low-cost route; deep uses the higher-reasoning route. + * @default standard + * @enum {string} + */ + analysis_depth: "standard" | "deep"; + /** + * @description Requested answer budget. Provider-reported output usage can include hidden reasoning tokens above this value; BeatAPI records the variance for audit and settles actual reported usage within the task reservation. + * @default 2048 + */ + max_output_tokens: number; + }; + MusicVideoTaskCreateRequest: components["schemas"]["StandardMusicVideoTaskCreateRequest"] | components["schemas"]["PremiumMusicVideoTaskCreateRequest"]; + StandardMusicVideoTaskCreateRequest: { + /** + * @description May be omitted to preserve the backwards-compatible Standard contract. + * @default standard + * @enum {string} + */ + mv_tier: "standard"; + /** @description Standard scene images. Provide 1-7 public HTTPS PNG, JPEG, or WebP URLs; place the primary subject or opening scene first. Upload local files through `POST /v1/files` and use the returned `data.url`. */ + images: string[]; + /** + * Format: uri + * @description Public HTTPS audio URL; Standard audio must be 10-180 seconds. + */ + audio_url: string; + /** @description Optional creative direction for story, setting, performance, camera, lighting, and pacing. Maximum 3000 characters. */ + prompt?: string; + /** + * @description Dialogue and lyric language used by the Standard workflow. + * @enum {string} + */ + language?: "en" | "zh"; + /** + * @description Generation quality tier. High quality is unavailable at 540p. + * @default standard + * @enum {string} + */ + quality: "standard" | "high"; + /** @description Optional concise visual style, such as cinematic, anime, documentary, or fashion editorial. */ + style?: string; + /** + * @description Target output placement. Set explicitly for the destination player or social feed. + * @enum {string} + */ + aspect_ratio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4"; + /** + * @description Output resolution. 540p cannot be combined with high quality or lip sync. + * @default 720p + * @enum {string} + */ + resolution: "540p" | "720p" | "1080p"; + /** + * @description Generate lip-synchronized performance. When true, `lip_ref_url` is required. + * @default false + */ + lip_sync: boolean; + /** + * Format: uri + * @description Public HTTPS close-up, front-facing face image used for Standard lip sync. + */ + lip_ref_url?: string; + /** + * @description Burn generated or supplied subtitles into the final video. + * @default false + */ + add_subtitle: boolean; + /** + * @description Subtitle text color as a six-digit hexadecimal value. Used when subtitles are enabled. + * @example #FFFFFF + */ + subtitle_color?: string; + /** + * Format: uri + * @description Optional public HTTPS `.srt` subtitle file. Upload a local subtitle through `POST /v1/files`. + */ + srt_url?: string; + /** @description Billing fallback only; detected audio duration wins. */ + duration?: number; + /** + * @description Auto composes the final Music Video; manual pauses at `requires_action` so shots can be reviewed or edited before compose. + * @default auto + * @enum {string} + */ + compose_mode: "auto" | "manual"; + } & (unknown & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + mv_tier: "standard"; + }); + PremiumMusicVideoTaskCreateRequest: (components["schemas"]["PremiumMusicVideoSingTaskCreateRequest"] | components["schemas"]["PremiumMusicVideoSingPerformTaskCreateRequest"] | components["schemas"]["PremiumMusicVideoDanceTaskCreateRequest"] | components["schemas"]["PremiumMusicVideoPerformTaskCreateRequest"]) & { + /** + * @description discriminator enum property added by openapi-typescript + * @enum {string} + */ + mv_tier: "premium"; + }; + PremiumMusicVideoTaskRequestBase: { + /** + * @description Selects the Premium Music Video workflow and its mode-specific inputs. + * @enum {string} + */ + mv_tier: "premium"; + /** + * @description Premium performance mode. Sing modes require `lip_ref_urls`; dance and perform require exactly six `images`. + * @enum {string} + */ + mv_mode: "sing" | "sing_perform" | "dance" | "perform"; + /** + * Format: uri + * @description Public HTTPS audio URL; Premium audio must be 10-300 seconds. + */ + audio_url: string; + /** @description Optional creative direction for story, setting, performance, camera, lighting, and pacing. Maximum 3000 characters. */ + prompt?: string; + style?: string; + /** + * @description Target output placement. Set explicitly for the destination player or social feed. + * @enum {string} + */ + aspect_ratio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4"; + /** + * @description Premium output is fixed to 720p. + * @default 720p + * @enum {string} + */ + resolution: "720p"; + /** + * @description Burn generated subtitles into the final video. + * @default false + */ + add_subtitle: boolean; + /** + * @description Subtitle text color as a six-digit hexadecimal value. Used when subtitles are enabled. + * @example #FFFFFF + */ + subtitle_color?: string; + /** @description Premium billing fallback only; detected audio duration wins. */ + duration?: number; + }; + PremiumMusicVideoSingTaskCreateRequest: components["schemas"]["PremiumMusicVideoTaskRequestBase"] & { + /** @enum {string} */ + mv_mode?: "sing"; + /** @description Optional Premium scene images for sing mode. Provide up to six public HTTPS PNG, JPEG, or WebP URLs. */ + images?: string[]; + /** @description Required for sing mode. One or two public HTTPS close-up, front-facing face images for lip synchronization. */ + lip_ref_urls: string[]; + }; + PremiumMusicVideoSingPerformTaskCreateRequest: components["schemas"]["PremiumMusicVideoTaskRequestBase"] & { + /** @enum {string} */ + mv_mode?: "sing_perform"; + /** @description Optional Premium scene images for sing and perform mode. Provide up to six public HTTPS PNG, JPEG, or WebP URLs. */ + images?: string[]; + /** @description Required for sing and perform mode. One or two public HTTPS close-up, front-facing face images for lip synchronization. */ + lip_ref_urls: string[]; + }; + PremiumMusicVideoDanceTaskCreateRequest: components["schemas"]["PremiumMusicVideoTaskRequestBase"] & { + /** @enum {string} */ + mv_mode?: "dance"; + /** @description Required for dance mode. Provide exactly six public HTTPS PNG, JPEG, or WebP scene images. */ + images: string[]; + }; + PremiumMusicVideoPerformTaskCreateRequest: components["schemas"]["PremiumMusicVideoTaskRequestBase"] & { + /** @enum {string} */ + mv_mode?: "perform"; + /** @description Required for perform mode. Provide exactly six public HTTPS PNG, JPEG, or WebP scene images. */ + images: string[]; + }; + EditMusicVideoShotRequest: { + prompt: string; + /** @description Premium tasks only. Optional replacement scene images; an empty array is treated as omitted. Standard tasks reject this field. */ + images?: string[]; + }; + RealtimeSession: { + /** @description Stable Realtime Session ID used to inspect or close the session. */ + id: string; + /** + * @description Object discriminator; always `realtime.session`. + * @enum {string} + */ + object: "realtime.session"; + /** + * @description Active means BeatAPI accepted the first billing heartbeat after remote output began. + * @enum {string} + */ + status: "ready" | "connecting" | "active" | "closed" | "failed" | "expired"; + /** + * Format: date-time + * @description Time when the unconnected short-lived session credential expires. + */ + expires_at: string; + /** + * @description Maximum selected live duration and billing tier in seconds. + * @enum {integer} + */ + max_duration_seconds: 15 | 60 | 300; + /** @description Exact browser origins authorized to use this Session. */ + allowed_origins: string[]; + /** @description USD reservation, settlement, and refund lifecycle for this Realtime Session. Compatibility field names are retained. */ + credits: { + /** + * Format: double + * @description USD amount reserved when the Session is created. + */ + reserved: number; + /** + * Format: double + * @description USD amount settled after the first accepted billing heartbeat. + */ + settled: number; + /** + * Format: double + * @description USD amount refunded if the Session ends without billing activation. + */ + refunded: number; + }; + /** @description Correlation ID to retain for logs and BeatAPI support. */ + request_id: string; + /** + * Format: date-time + * @description Time when the Session was created. + */ + created_at: string; + /** + * Format: date-time + * @description Time of the first accepted BeatAPI billing heartbeat; null before billing activation. + */ + connected_at: string | null; + /** + * Format: date-time + * @description Time when the Session closed, or null while it remains open. + */ + closed_at: string | null; + }; + RealtimeSessionCreated: components["schemas"]["RealtimeSession"] & { + /** @description Short-lived BeatAPI browser credential returned only by POST. Never expose an sk_ API key to the browser. */ + client_secret: string; + }; + RealtimeSessionCreateResponse: { + /** @description Created Realtime Session including the one-time short-lived browser credential. */ + data: components["schemas"]["RealtimeSessionCreated"]; + }; + RealtimeSessionResponse: { + data: components["schemas"]["RealtimeSession"]; + }; + FileResponse: { + /** @description Uploaded file metadata and the public HTTPS URL to use in later requests. */ + data: components["schemas"]["File"]; + }; + WebhookEndpointList: { + /** @enum {string} */ + object: "list"; + data: components["schemas"]["WebhookEndpoint"][]; + }; + WebhookEndpointListResponse: { + data: components["schemas"]["WebhookEndpointList"]; + }; + WebhookEndpointResponse: { + /** @description Created or retrieved webhook endpoint. Public API responses return the full signing secret at creation and mask it afterward; authenticated dashboard owners can explicitly reveal it again. */ + data: components["schemas"]["WebhookEndpoint"]; + }; + DeleteResponse: { + data: { + id: string; + deleted: boolean; + }; + }; + Error: { + /** @description Structured BeatAPI error. Use `code` for program logic and retain `request_id` for support. */ + error: { + /** + * @description Stable machine-readable error code. + * @enum {string} + */ + code: "bad_request" | "unauthorized" | "forbidden" | "not_found" | "insufficient_credits" | "idempotency_conflict" | "user_concurrency_exceeded" | "rate_limit_exceeded" | "content_policy_violation" | "processing_unavailable" | "processing_failed" | "processing_timeout" | "result_transfer_failed" | "invalid_signature" | "realtime_disabled" | "realtime_capacity_unavailable" | "realtime_session_expired" | "origin_not_allowed" | "invalid_client_secret" | "transport_not_allowed" | "internal_error"; + /** @description Human-readable detail intended for logs and debugging. */ + message: string; + /** @description Correlation ID to retain for BeatAPI support. */ + request_id: string; + /** @description Present on retryable rate-limit or capacity responses when the client should wait before retrying. */ + retry_after_seconds?: number; + }; + }; + }; + responses: { + /** @description Missing, invalid, or inactive API key. */ + Unauthorized: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "error": { + * "code": "unauthorized", + * "message": "Missing or invalid API key.", + * "request_id": "req_xxx" + * } + * } + */ + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Invalid request. */ + BadRequest: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "error": { + * "code": "bad_request", + * "message": "images must contain 1-7 public HTTPS URLs.", + * "request_id": "req_xxx" + * } + * } + */ + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Request rate limit exceeded. */ + RateLimited: { + headers: { + /** @description Seconds to wait before retrying the request. */ + "Retry-After"?: number; + [name: string]: unknown; + }; + content: { + /** + * @example { + * "error": { + * "code": "rate_limit_exceeded", + * "message": "Too many polling requests. Poll every 5-10 seconds.", + * "request_id": "req_xxx", + * "retry_after_seconds": 12 + * } + * } + */ + "application/json": components["schemas"]["Error"]; }; }; - /** @description Invalid request. */ - BadRequest: { + /** @description BeatAPI could not complete the request because of an internal or storage failure. */ + InternalError: { headers: { [name: string]: unknown; }; @@ -744,8 +2395,8 @@ export interface components { /** * @example { * "error": { - * "code": "bad_request", - * "message": "images must contain 1-7 public HTTPS URLs.", + * "code": "internal_error", + * "message": "Internal error. Contact support with the request_id if the problem continues.", * "request_id": "req_xxx" * } * } @@ -753,35 +2404,397 @@ export interface components { "application/json": components["schemas"]["Error"]; }; }; - /** @description Request rate limit exceeded. */ - RateLimited: { + /** @description BeatAPI processing is temporarily unavailable or did not complete within the processing window. */ + ProcessingUnavailable: { headers: { - /** @description Seconds to wait before retrying the request. */ - "Retry-After"?: number; [name: string]: unknown; }; - content: { - /** - * @example { - * "error": { - * "code": "rate_limit_exceeded", - * "message": "Too many polling requests. Poll every 5-10 seconds.", - * "request_id": "req_xxx", - * "retry_after_seconds": 12 - * } - * } - */ - "application/json": components["schemas"]["Error"]; + content: { + /** + * @example { + * "error": { + * "code": "processing_unavailable", + * "message": "Task processing is temporarily unavailable.", + * "request_id": "req_xxx" + * } + * } + */ + "application/json": components["schemas"]["Error"]; + }; + }; + }; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + listTextModels: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OpenAI-compatible model list */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "object": "list", + * "data": [ + * { + * "id": "gpt-5.6-sol", + * "object": "model", + * "created": 1788220800, + * "owned_by": "beatapi" + * }, + * { + * "id": "gpt-5.6-terra", + * "object": "model", + * "created": 1788220800, + * "owned_by": "beatapi" + * }, + * { + * "id": "gpt-5.6-luna", + * "object": "model", + * "created": 1788220800, + * "owned_by": "beatapi" + * }, + * { + * "id": "claude-fable-5-1", + * "object": "model", + * "created": 1788220800, + * "owned_by": "beatapi" + * } + * ] + * } + */ + "application/json": components["schemas"]["TextModelList"]; + }; + }; + /** @description Invalid or missing BeatAPI API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text API is not enabled for this environment */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Request rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + createTextResponse: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "model": "gpt-5.6-sol", + * "input": "Design a resilient webhook retry strategy for a payments API.", + * "reasoning": { + * "effort": "medium" + * }, + * "stream": true + * } + */ + "application/json": components["schemas"]["TextPassthroughRequest"]; + }; + }; + responses: { + /** @description OpenAI-compatible JSON response or server-sent event stream */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TextPassthroughResponse"]; + "text/event-stream": string; + }; + }; + /** @description Invalid or missing BeatAPI API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient BeatAPI USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit or settlement backlog */ + 429: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text gateway could not complete the request */ + 502: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text service is temporarily unavailable */ + 503: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + createChatCompletion: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "model": "gpt-5.6-terra", + * "messages": [ + * { + * "role": "user", + * "content": "Summarize the attached product requirements into an implementation plan." + * } + * ], + * "stream": true + * } + */ + "application/json": components["schemas"]["TextPassthroughRequest"]; + }; + }; + responses: { + /** @description OpenAI-compatible JSON response or server-sent event stream */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TextPassthroughResponse"]; + "text/event-stream": string; + }; + }; + /** @description Invalid or missing BeatAPI API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient BeatAPI USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit or settlement backlog */ + 429: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text gateway could not complete the request */ + 502: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text service is temporarily unavailable */ + 503: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + createMessage: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "model": "gpt-5.6-luna", + * "max_tokens": 1024, + * "messages": [ + * { + * "role": "user", + * "content": "Classify this support request and return JSON." + * } + * ] + * } + */ + "application/json": components["schemas"]["TextPassthroughRequest"]; + }; + }; + responses: { + /** @description Anthropic-compatible JSON response or server-sent event stream */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TextPassthroughResponse"]; + "text/event-stream": string; + }; + }; + /** @description Invalid or missing BeatAPI API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient BeatAPI USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit or settlement backlog */ + 429: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text gateway could not complete the request */ + 502: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text service is temporarily unavailable */ + 503: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + generateGeminiCompatibleContent: { + parameters: { + query?: never; + header?: never; + path: { + model: components["schemas"]["TextModelId"]; + action: "generateContent" | "streamGenerateContent"; + }; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "contents": [ + * { + * "role": "user", + * "parts": [ + * { + * "text": "Explain this architecture decision in three concise bullets." + * } + * ] + * } + * ] + * } + */ + "application/json": { + [key: string]: unknown; + }; + }; + }; + responses: { + /** @description Gemini-compatible JSON response or server-sent event stream */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TextPassthroughResponse"]; + "text/event-stream": string; + }; + }; + /** @description Invalid or missing BeatAPI API key */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Insufficient BeatAPI USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Rate limit or settlement backlog */ + 429: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text gateway could not complete the request */ + 502: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Text service is temporarily unavailable */ + 503: { + headers: { + [name: string]: unknown; + }; + content?: never; }; }; }; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { listWorkflows: { parameters: { query?: never; @@ -813,6 +2826,12 @@ export interface operations { * "object": "workflow", * "name": "Ecommerce Video API", * "description": "Generate product ad videos from product images and a short creative brief." + * }, + * { + * "id": "video-analysis", + * "object": "workflow", + * "name": "Video Analysis API", + * "description": "Analyze an uploaded video with timestamp-aware multimodal reasoning." * } * ] * } @@ -824,77 +2843,319 @@ export interface operations { 429: components["responses"]["RateLimited"]; }; }; - createMusicVideoTask: { + listGenerationModels: { + parameters: { + query?: { + media_type?: "image" | "video"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Generation model list */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GenerationModelListResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 429: components["responses"]["RateLimited"]; + }; + }; + createImageGenerationTask: { + parameters: { + query?: never; + header?: { + "Idempotency-Key"?: string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["ImageGenerationTaskCreateRequest"]; + }; + }; + responses: { + /** @description Image generation task accepted */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TaskResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 401: components["responses"]["Unauthorized"]; + /** @description Insufficient USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Idempotency key conflicts with another request body */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + }; + }; + createVideoGenerationTask: { + parameters: { + query?: never; + header?: { + "Idempotency-Key"?: string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["VideoGenerationTaskCreateRequest"]; + }; + }; + responses: { + /** @description Video generation task accepted */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TaskResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 401: components["responses"]["Unauthorized"]; + /** @description Insufficient USD balance */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Idempotency key conflicts with another request body */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + }; + }; + listEffects: { + parameters: { + query?: { + output_type?: "image" | "video"; + category?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Active Effect catalog */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EffectListResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 429: components["responses"]["RateLimited"]; + }; + }; + getEffect: { parameters: { query?: never; header?: never; + path: { + effect_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Effect definition and immutable current version contract */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EffectResponse"]; + }; + }; + /** @description Effect is unknown or not currently published. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + createEffectTask: { + parameters: { + query?: never; + header?: { + "Idempotency-Key"?: string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "effect_id": "video-muscle-max", + * "images": [ + * "https://media.beatapi.io/samples/portrait.png" + * ], + * "options": { + * "resolution": "720p", + * "duration": 12 + * } + * } + */ + "application/json": { + /** + * @description Stable published Effect ID from `GET /v1/effects`. + * @example video-muscle-max + */ + effect_id: string; + /** @description Optional immutable version. Omit to use the current published version. */ + effect_version?: number; + /** @description Public HTTPS input images in the order required by the selected Effect version. Read `GET /v1/effects/{effect_id}` for the exact count and accepted media rules; upload local files with `POST /v1/files`. */ + images: string[]; + /** @description Optional controls supported by the selected Effect version. Omit unsupported controls; the catalog is the source of truth. */ + options?: { + /** @description Requested output aspect ratio when the selected Effect exposes this option. */ + aspect_ratio?: string; + /** @description Requested output resolution when the selected Effect exposes this option. */ + resolution?: string; + /** @description Requested video duration in seconds when the selected Effect exposes this option. */ + duration?: number; + /** @description Include background music when supported by the selected Effect. */ + bgm?: boolean; + /** @description Optional deterministic seed when supported by the selected Effect. */ + seed?: number; + }; + }; + }; + }; + responses: { + /** @description Effect task accepted */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "data": { + * "id": "task_effect123", + * "object": "task", + * "task_kind": "effect", + * "capability_id": "video-muscle-max", + * "capability_version": 1, + * "effect_id": "video-muscle-max", + * "effect_version": 1, + * "status": "queued", + * "stage": "queued", + * "created_at": 1782210000, + * "updated_at": 1782210000, + * "completed_at": null, + * "output": null, + * "usage": { + * "credits_reserved": 1.2, + * "credits_charged": 1.2, + * "credits_settled": 0, + * "credits_refunded": 0 + * }, + * "request_id": "req_effect123", + * "error_code": null, + * "error_message": null + * } + * } + */ + "application/json": components["schemas"]["TaskResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 401: components["responses"]["Unauthorized"]; + /** @description Insufficient USD balance. */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Effect or requested version is unavailable. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Idempotency key conflicts with another request body. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + }; + }; + createVideoAnalysisTask: { + parameters: { + query?: never; + header?: { + /** + * @description Optional retry key. Reusing the same key with the same normalized request returns the accepted task. + * @example video-analysis-cus_123-01 + */ + "Idempotency-Key"?: string; + }; path?: never; cookie?: never; }; requestBody: { content: { - /** - * @example { - * "images": [ - * "https://media.beatapi.io/samples/neon-singer.png" - * ], - * "audio_url": "https://media.beatapi.io/samples/neon-singer-preview.mp3", - * "prompt": "Neon rooftop performance with metro cutaways and cinematic light trails.", - * "language": "en", - * "quality": "standard", - * "resolution": "720p", - * "compose_mode": "auto" - * } - */ - "application/json": { - /** @description 1-7 public HTTPS image URLs. Use png, jpg, jpeg, or webp images; each image should be 50 MB or smaller, with aspect ratio from 1:4 to 4:1. /v1/files uploads are checked before use; third-party URLs may be rejected during processing if invalid. */ - images: string[]; - /** - * Format: uri - * @description Public HTTPS audio URL. Use mp3, wav, aac, or m4a; file size should be 50 MB or smaller and duration must be 10-180 seconds. - */ - audio_url: string; - /** @description Optional creative prompt, at most 3000 characters. */ - prompt?: string; - /** @enum {string} */ - language?: "en" | "zh"; - lip_sync?: boolean; - /** - * Format: uri - * @description Public HTTPS image URL for lip-sync face reference. Use a clear, front-facing close-up face reference. - */ - lip_ref_url?: string; - /** @description Optional style phrase, at most 200 characters. */ - style?: string; - /** - * @default standard - * @enum {string} - */ - quality?: "standard" | "high"; - /** @enum {string} */ - aspect_ratio?: "1:1" | "16:9" | "9:16" | "4:3" | "3:4"; - /** - * @default 720p - * @enum {string} - */ - resolution?: "540p" | "720p" | "1080p"; - add_subtitle?: boolean; - /** @example #FFFFFF */ - subtitle_color?: string; - /** Format: uri */ - srt_url?: string; - /** @description Billing fallback when audio duration cannot be detected. It must be 10-180 seconds and cannot override a detected audio duration. */ - duration?: number; - /** - * @default auto - * @enum {string} - */ - compose_mode?: "auto" | "manual"; - }; + "application/json": components["schemas"]["VideoAnalysisTaskCreateRequest"]; }; }; responses: { - /** @description Task accepted */ + /** @description Analysis accepted for asynchronous processing. */ 201: { headers: { [name: string]: unknown; @@ -903,26 +3164,26 @@ export interface operations { /** * @example { * "data": { - * "id": "task_8K2qA", + * "id": "task_va8K2qA", * "object": "task", - * "workflow": "music-video", + * "task_kind": "workflow", + * "capability_id": "video-analysis", + * "capability_version": 1, + * "workflow": "video-analysis", * "status": "queued", * "stage": "queued", - * "storyboard": { - * "shots": [] - * }, - * "created_at": 1782210000, - * "updated_at": 1782210000, + * "created_at": 1787385600, + * "updated_at": 1787385600, * "completed_at": null, * "output": null, * "usage": { - * "credits_reserved": 75, - * "credits_charged": 75, - * "billable_duration_seconds": 15, + * "credits_reserved": 0.01, + * "credits_charged": 0.01, + * "billable_duration_seconds": 60, * "credits_settled": 0, * "credits_refunded": 0 * }, - * "request_id": "req_abc123", + * "request_id": "req_va123", * "error_code": null, * "error_message": null * } @@ -933,6 +3194,57 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + /** @description Account balance is not sufficient for the reserved analysis envelope. */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Idempotency key conflicts with another request body. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + }; + }; + createMusicVideoTask: { + parameters: { + query?: never; + header?: { + /** + * @description Optional retry key. Reusing the same key with the same request body returns the accepted task; reusing it with a different body returns `409 idempotency_conflict`. + * @example mv-create-cus_123-01 + */ + "Idempotency-Key"?: string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MusicVideoTaskCreateRequest"]; + }; + }; + responses: { + /** @description Task accepted */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TaskResponse"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 401: components["responses"]["Unauthorized"]; /** @description Account balance is not sufficient for this task. */ 402: { headers: { @@ -951,6 +3263,24 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + /** @description The Idempotency-Key was reused with a different body or while another request with that key is still being processed. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "error": { + * "code": "idempotency_conflict", + * "message": "This Idempotency-Key was already used with a different request body.", + * "request_id": "req_xxx" + * } + * } + */ + "application/json": components["schemas"]["Error"]; + }; + }; /** @description User concurrency exceeded. */ 429: { headers: { @@ -974,7 +3304,13 @@ export interface operations { editMusicVideoShot: { parameters: { query?: never; - header?: never; + header?: { + /** + * @description Optional retry key. Reusing the same key for this task, shot, and request body returns the accepted task without charging the USD amount again; changing any of them returns `409 idempotency_conflict`. + * @example music-edit-task_8K2qA-shot_xxx-01 + */ + "Idempotency-Key"?: string; + }; path: { /** @example task_8K2qA */ task_id: string; @@ -988,26 +3324,12 @@ export interface operations { /** * @example { * "prompt": "Night city chorus with brighter face lighting.", - * "duration": 5, - * "quality": "standard", - * "resolution": "720p" + * "images": [ + * "https://media.beatapi.io/samples/stage.png" + * ] * } */ - "application/json": { - prompt: string; - /** @default 5 */ - duration?: number; - /** - * @default standard - * @enum {string} - */ - quality?: "standard" | "high"; - /** - * @default 720p - * @enum {string} - */ - resolution?: "540p" | "720p" | "1080p"; - }; + "application/json": components["schemas"]["EditMusicVideoShotRequest"]; }; }; responses: { @@ -1022,6 +3344,15 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + /** @description Account balance is not sufficient for this shot edit. */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; /** @description Task or shot not found. */ 404: { headers: { @@ -1031,6 +3362,18 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + /** @description The Idempotency-Key was reused for a different task, shot, or request body, or the same request is still being processed. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; + 502: components["responses"]["ProcessingUnavailable"]; }; }; getMusicVideoShotMedia: { @@ -1088,12 +3431,21 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; + 502: components["responses"]["ProcessingUnavailable"]; }; }; composeMusicVideoTask: { parameters: { query?: never; - header?: never; + header?: { + /** + * @description Optional retry key. Reusing the same key for this task and request body returns the accepted task without charging the $1 compose amount again; changing either returns `409 idempotency_conflict`. + * @example music-compose-task_8K2qA-01 + */ + "Idempotency-Key"?: string; + }; path: { /** @example task_8K2qA */ task_id: string; @@ -1127,6 +3479,15 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + /** @description Account balance is not sufficient for this compose operation. */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; /** @description Task or shot not found. */ 404: { headers: { @@ -1136,12 +3497,30 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + /** @description The Idempotency-Key was reused for a different task or request body, or the same request is still being processed. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; + 502: components["responses"]["ProcessingUnavailable"]; }; }; createEcommerceVideoTask: { parameters: { query?: never; - header?: never; + header?: { + /** + * @description Optional retry key. Reusing the same key with the same request body returns the accepted task; reusing it with a different body returns `409 idempotency_conflict`. + * @example ecommerce-create-cus_123-01 + */ + "Idempotency-Key"?: string; + }; path?: never; cookie?: never; }; @@ -1158,12 +3537,21 @@ export interface operations { * } */ "application/json": { + /** @description Primary product or scene image first, followed by up to six additional public HTTPS PNG, JPEG, or WebP product or lifestyle images. Upload local files with `POST /v1/files` and use the returned `data.url`. */ images: string[]; + /** @description Required target output duration in seconds and the basis for USD calculation. Allowed range is 10-60 seconds. */ duration: number; + /** @description Optional creative direction, audience, product benefit, offer, tone, scenes, or call to action. Maximum 2000 characters. */ prompt?: string; - /** @enum {string} */ + /** + * @description Target output placement. Use 16:9 for landscape, 9:16 for vertical social, or 1:1 for square placements; set explicitly for stable layout. + * @enum {string} + */ aspect_ratio?: "16:9" | "9:16" | "1:1"; - /** @enum {string} */ + /** + * @description Dialogue and narration language. Use `en` for English or `zh` for Chinese; set explicitly when the prompt contains mixed languages. + * @enum {string} + */ language?: "en" | "zh"; }; }; @@ -1180,6 +3568,9 @@ export interface operations { * "data": { * "id": "task_p9Lm2", * "object": "task", + * "task_kind": "workflow", + * "capability_id": "ecommerce-video", + * "capability_version": 1, * "workflow": "ecommerce-video", * "status": "queued", * "stage": "queued", @@ -1188,8 +3579,8 @@ export interface operations { * "completed_at": null, * "output": null, * "usage": { - * "credits_reserved": 225, - * "credits_charged": 225, + * "credits_reserved": 4.5, + * "credits_charged": 4.5, * "billable_duration_seconds": 15, * "credits_settled": 0, * "credits_refunded": 0 @@ -1223,6 +3614,24 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + /** @description The Idempotency-Key was reused with a different body or while another request with that key is still being processed. */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + /** + * @example { + * "error": { + * "code": "idempotency_conflict", + * "message": "This Idempotency-Key was already used with a different request body.", + * "request_id": "req_xxx" + * } + * } + */ + "application/json": components["schemas"]["Error"]; + }; + }; /** @description User concurrency exceeded. */ 429: { headers: { @@ -1281,6 +3690,7 @@ export interface operations { parameters: { query?: never; header: { + /** @example rts-create-cus_123-01 */ "Idempotency-Key": string; }; path?: never; @@ -1300,9 +3710,14 @@ export interface operations { * } */ "application/json": { - /** @enum {integer} */ + /** + * @description Required maximum live session duration in seconds. The USD amount is reserved for the selected 15, 60, or 300 second tier. + * @enum {integer} + */ max_duration_seconds: 15 | 60 | 300; + /** @description Exact browser origins allowed to use the short-lived session secret. */ allowed_origins: string[]; + /** @description Optional server-defined string metadata for your own correlation. Up to 20 keys; keys are at most 64 characters and values at most 256 characters. */ metadata?: { [key: string]: string; }; @@ -1316,12 +3731,36 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["RealtimeSessionResponse"]; + /** + * @example { + * "data": { + * "id": "rts_8K2qA", + * "object": "realtime.session", + * "status": "ready", + * "client_secret": "brt_live_example_short_lived_secret", + * "expires_at": "2026-08-12T10:01:00.000Z", + * "max_duration_seconds": 60, + * "allowed_origins": [ + * "https://app.example.com" + * ], + * "credits": { + * "reserved": 1.2, + * "settled": 0, + * "refunded": 0 + * }, + * "request_id": "req_abc123", + * "created_at": "2026-08-12T10:00:00.000Z", + * "connected_at": null, + * "closed_at": null + * } + * } + */ + "application/json": components["schemas"]["RealtimeSessionCreateResponse"]; }; }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; - /** @description Insufficient credits */ + /** @description Insufficient USD balance */ 402: { headers: { [name: string]: unknown; @@ -1434,29 +3873,60 @@ export interface operations { * @example { * "data": { * "object": "usage", - * "credit_balance": 1080, + * "credit_balance": 21.6, * "total_tasks": 12, - * "credits_settled": 720, - * "credits_refunded": 450, + * "credits_settled": 14.4, + * "credits_refunded": 9, * "concurrency": { * "limit": 2, * "active": 1 * }, * "realtime": { * "sessions": 3, - * "credits": 90, + * "credits": 1.8, * "active": 1 * }, * "by_workflow": [ * { * "workflow": "music-video", * "tasks": 8, - * "credits_settled": 480 + * "credits_settled": 9.6 * }, * { * "workflow": "ecommerce-video", * "tasks": 4, - * "credits_settled": 240 + * "credits_settled": 4.8 + * } + * ], + * "by_capability": [ + * { + * "task_kind": "image", + * "capability_id": "seedream-5-pro", + * "tasks": 3, + * "credits_settled": 0.42 + * }, + * { + * "task_kind": "video", + * "capability_id": "veo-3.1", + * "tasks": 2, + * "credits_settled": 14 + * } + * ], + * "by_model": [ + * { + * "media_type": "image", + * "model": "seedream-5-pro", + * "tasks": 3, + * "credits_settled": 0.42 + * } + * ], + * "by_api_key": [ + * { + * "api_key_id": "key_abc123", + * "title": "Production", + * "key_prefix": "sk_live_abcd", + * "tasks": 12, + * "credits_settled": 14.4 * } * ] * } @@ -1471,7 +3941,10 @@ export interface operations { uploadFile: { parameters: { query?: never; - header?: never; + header: { + /** @description Exact request-body length in bytes. For multipart uploads this includes multipart framing overhead. */ + "Content-Length": number; + }; path?: never; cookie?: never; }; @@ -1483,6 +3956,16 @@ export interface operations { /** @enum {string} */ purpose?: "input"; }; + "image/png": string; + "image/jpeg": string; + "image/webp": string; + "audio/mpeg": string; + "audio/wav": string; + "audio/aac": string; + "audio/mp4": string; + "video/mp4": string; + "video/quicktime": string; + "application/x-subrip": string; }; }; responses: { @@ -1513,6 +3996,8 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; }; }; listWebhookEndpoints: { @@ -1557,6 +4042,8 @@ export interface operations { }; }; 401: components["responses"]["Unauthorized"]; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; }; }; createWebhookEndpoint: { @@ -1578,9 +4065,14 @@ export interface operations { * } */ "application/json": { - /** Format: uri */ + /** + * Format: uri + * @description Public HTTPS callback URL that accepts BeatAPI task events. Do not use localhost or a private-network URL. + */ url: string; + /** @description Optional internal label for identifying the endpoint in your account. */ description?: string; + /** @description Task events to deliver. Omit to subscribe to both `task.succeeded` and `task.failed`. */ events?: ("task.succeeded" | "task.failed")[]; }; }; @@ -1615,6 +4107,8 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; }; }; getWebhookEndpoint: { @@ -1666,6 +4160,8 @@ export interface operations { "application/json": components["schemas"]["Error"]; }; }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; }; }; deleteWebhookEndpoint: { @@ -1698,6 +4194,17 @@ export interface operations { }; }; 401: components["responses"]["Unauthorized"]; + /** @description Webhook endpoint not found. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; }; }; updateWebhookEndpoint: { @@ -1718,7 +4225,10 @@ export interface operations { * } */ "application/json": { - /** Format: uri */ + /** + * Format: uri + * @description Public HTTPS callback URL that accepts BeatAPI task events. Do not use localhost or a private-network URL. + */ url?: string; description?: string; /** @enum {string} */ @@ -1739,6 +4249,84 @@ export interface operations { }; 400: components["responses"]["BadRequest"]; 401: components["responses"]["Unauthorized"]; + /** @description Webhook endpoint not found. */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; + }; + }; + receiveBeatApiTaskEvent: { + parameters: { + query?: never; + header: { + "x-beatapi-event": "task.succeeded" | "task.failed"; + "x-beatapi-timestamp": string; + "x-beatapi-signature": string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "id": "evt_123", + * "event": "task.succeeded", + * "created_at": 1782210300, + * "data": { + * "id": "task_8K2qA", + * "object": "task", + * "task_kind": "video", + * "capability_id": "seedance-2.5", + * "capability_version": null, + * "media_type": "video", + * "model": "seedance-2.5", + * "status": "succeeded", + * "stage": "succeeded", + * "created_at": 1782210000, + * "updated_at": 1782210300, + * "completed_at": 1782210300, + * "output": { + * "media": [ + * { + * "type": "video", + * "url": "https://media.beatapi.io/outputs/task_8K2qA/0.mp4", + * "mime_type": "video/mp4" + * } + * ], + * "r2_url": "https://media.beatapi.io/outputs/task_8K2qA/0.mp4" + * }, + * "usage": { + * "credits_reserved": 1.55, + * "credits_charged": 1.55, + * "billable_duration_seconds": 5, + * "credits_settled": 1.55, + * "credits_refunded": 0 + * }, + * "request_id": "req_abc123", + * "error_code": null, + * "error_message": null + * } + * } + */ + "application/json": components["schemas"]["WebhookEvent"]; + }; + }; + responses: { + /** @description Event accepted */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; }; } diff --git a/packages/client/test/client.test.ts b/packages/client/test/client.test.ts index 85ec2ab..fbbf96a 100644 --- a/packages/client/test/client.test.ts +++ b/packages/client/test/client.test.ts @@ -44,6 +44,192 @@ test("parses success envelopes and sends bearer authentication", async () => { assert.equal(authorization, "Bearer sk_test_value"); }); +test("lists the authenticated text-model catalog in its OpenAI-compatible shape", async () => { + let authorization = ""; + const client = new BeatAPIClient({ + apiKey: "sk_test_value", + fetch: async (_input, init) => { + authorization = new Headers(init?.headers).get("authorization") || ""; + return jsonResponse({ + object: "list", + data: [ + { id: "gpt-5.6-terra", object: "model", created: 1, owned_by: "beatapi" }, + ], + }); + }, + }); + + const models = await client.listTextModels(); + + assert.equal(models[0]?.id, "gpt-5.6-terra"); + assert.equal(authorization, "Bearer sk_test_value"); +}); + +test("creates a non-streaming text response without unwrapping the provider payload", async () => { + let request: { path: string; body: unknown } | undefined; + const client = new BeatAPIClient({ + apiKey: "sk_test_value", + fetch: async (input, init) => { + request = { + path: new URL(String(input)).pathname, + body: JSON.parse(String(init?.body)) as unknown, + }; + return jsonResponse({ + id: "resp_test", + object: "response", + output_text: "A concise answer.", + }); + }, + }); + + const response = await client.createTextResponse({ + model: "gpt-5.6-terra", + input: "Summarize this.", + stream: false, + }); + + assert.deepEqual(request, { + path: "/v1/responses", + body: { + model: "gpt-5.6-terra", + input: "Summarize this.", + stream: false, + }, + }); + assert.equal((response as { output_text: string }).output_text, "A concise answer."); +}); + +test("creates a video-analysis task with an idempotency key", async () => { + let request: { path: string; idempotencyKey: string | null; body: unknown } | undefined; + const client = new BeatAPIClient({ + apiKey: "sk_test_value", + fetch: async (input, init) => { + request = { + path: new URL(String(input)).pathname, + idempotencyKey: new Headers(init?.headers).get("idempotency-key"), + body: JSON.parse(String(init?.body)) as unknown, + }; + return jsonResponse({ data: { id: "task_analysis", status: "queued" } }); + }, + }); + + const task = await client.createVideoAnalysisTask( + { + video_url: "https://media.example.com/input.mp4", + prompt: "Return timestamped scene changes.", + analysis_depth: "deep", + }, + { idempotencyKey: "analysis-test" }, + ); + + assert.equal(task.id, "task_analysis"); + assert.deepEqual(request, { + path: "/v1/video-analysis/tasks", + idempotencyKey: "analysis-test", + body: { + video_url: "https://media.example.com/input.mp4", + prompt: "Return timestamped scene changes.", + analysis_depth: "deep", + }, + }); +}); + +test("discovers generation models and creates image and video tasks", async () => { + const requests: Array<{ method: string; path: string; authorization: string | null }> = []; + const client = new BeatAPIClient({ + apiKey: "sk_test_value", + fetch: async (input, init) => { + const path = new URL(String(input)).pathname; + requests.push({ + method: init?.method || "GET", + path, + authorization: new Headers(init?.headers).get("authorization"), + }); + if (path === "/v1/media/models") { + return jsonResponse({ + data: { + object: "list", + data: [ + { + id: "nano-banana-2", + object: "generation_model", + name: "Nano Banana 2", + media_type: "image", + input_modes: ["text", "image"], + }, + ], + }, + }); + } + return jsonResponse({ data: { id: "task_media", status: "queued" } }); + }, + }); + + const models = await client.listGenerationModels(); + await client.createImageTask({ model: "nano-banana-2", prompt: "Still" }); + await client.createVideoTask({ model: "seedance-2.5", prompt: "Motion" }); + + assert.equal(models[0]?.id, "nano-banana-2"); + assert.deepEqual(requests, [ + { method: "GET", path: "/v1/media/models", authorization: null }, + { method: "POST", path: "/v1/images/tasks", authorization: "Bearer sk_test_value" }, + { method: "POST", path: "/v1/videos/tasks", authorization: "Bearer sk_test_value" }, + ]); +}); + +test("discovers Effect contracts and creates a versioned Effect task", async () => { + const requests: Array<{ method: string; path: string; query: string; idempotencyKey: string | null }> = []; + const client = new BeatAPIClient({ + apiKey: "sk_test_value", + fetch: async (input, init) => { + const url = new URL(String(input)); + requests.push({ + method: init?.method || "GET", + path: url.pathname, + query: url.search, + idempotencyKey: new Headers(init?.headers).get("idempotency-key"), + }); + if (url.pathname === "/v1/effects") { + return jsonResponse({ data: { object: "list", data: [{ id: "muscle", version: 1 }] } }); + } + if (url.pathname === "/v1/effects/muscle") { + return jsonResponse({ data: { id: "muscle", version: 1 } }); + } + return jsonResponse({ data: { id: "task_effect", status: "queued" } }); + }, + }); + + const effects = await client.listEffects({ outputType: "video", category: "transformation" }); + const effect = await client.getEffect("muscle"); + const task = await client.createEffectTask( + { + effect_id: "muscle", + effect_version: 1, + images: ["https://media.example.com/portrait.png"], + }, + { idempotencyKey: "effect-test" }, + ); + + assert.equal(effects[0]?.id, "muscle"); + assert.equal(effect.version, 1); + assert.equal(task.id, "task_effect"); + assert.deepEqual(requests, [ + { + method: "GET", + path: "/v1/effects", + query: "?output_type=video&category=transformation", + idempotencyKey: null, + }, + { method: "GET", path: "/v1/effects/muscle", query: "", idempotencyKey: null }, + { + method: "POST", + path: "/v1/effects/tasks", + query: "", + idempotencyKey: "effect-test", + }, + ]); +}); + test("preserves structured errors, request id, and retry-after", async () => { const client = new BeatAPIClient({ apiKey: "sk_test_value", From bb7c730ac1022ab8dd47d7f52d127c544ceda4a9 Mon Sep 17 00:00:00 2001 From: KKKK Date: Tue, 8 Sep 2026 22:38:23 +0800 Subject: [PATCH 2/3] Validate custom API client origins --- packages/client/src/client.ts | 49 +++++++++++++++++++++++++++-- packages/client/test/client.test.ts | 27 ++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index c0edccf..ea37450 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -54,6 +54,8 @@ export interface RetryOptions { export interface BeatAPIClientOptions { apiKey?: string | undefined; baseUrl?: string | undefined; + allowInsecureLocalhost?: boolean | undefined; + trustCustomBaseUrl?: boolean | undefined; fetch?: FetchLike | undefined; sleep?: ((milliseconds: number) => Promise) | undefined; random?: (() => number) | undefined; @@ -99,6 +101,47 @@ const ACTIONABLE_OR_TERMINAL_STATUSES = new Set([ const RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504]); +function validatedBaseUrl( + value: string, + options: Pick< + BeatAPIClientOptions, + "allowInsecureLocalhost" | "trustCustomBaseUrl" + >, +): string { + let parsed: URL; + try { + parsed = new URL(value); + } catch { + throw new TypeError("BeatAPI base URL must be an exact HTTPS origin."); + } + const isLoopback = ["localhost", "127.0.0.1", "[::1]"].includes( + parsed.hostname, + ); + const insecureTestOrigin = options.allowInsecureLocalhost === true && isLoopback; + if ( + (parsed.protocol !== "https:" && !insecureTestOrigin) || + parsed.username || + parsed.password || + parsed.pathname !== "/" || + parsed.search || + parsed.hash + ) { + throw new TypeError( + "BeatAPI base URL must be an exact HTTPS origin without credentials, path, query, or fragment.", + ); + } + if ( + parsed.origin !== "https://api.beatapi.io" && + !insecureTestOrigin && + options.trustCustomBaseUrl !== true + ) { + throw new TypeError( + "A custom BeatAPI HTTPS origin requires an explicit trusted operator setting.", + ); + } + return parsed.origin; +} + function assertPositiveInteger(value: number, label: string): void { if (!Number.isInteger(value) || value <= 0) { throw new TypeError(`${label} must be a positive integer.`); @@ -179,9 +222,9 @@ export class BeatAPIClient { constructor(options: BeatAPIClientOptions = {}) { this.apiKey = options.apiKey; - this.baseUrl = (options.baseUrl || "https://api.beatapi.io").replace( - /\/+$/, - "", + this.baseUrl = validatedBaseUrl( + options.baseUrl || "https://api.beatapi.io", + options, ); const fetchImpl = options.fetch ?? globalThis.fetch; if (typeof fetchImpl !== "function") { diff --git a/packages/client/test/client.test.ts b/packages/client/test/client.test.ts index fbbf96a..c925313 100644 --- a/packages/client/test/client.test.ts +++ b/packages/client/test/client.test.ts @@ -18,6 +18,33 @@ function jsonResponse( }); } +test("rejects unsafe API origins unless an operator explicitly trusts HTTPS", () => { + assert.throws( + () => new BeatAPIClient({ apiKey: "test", baseUrl: "http://example.com" }), + /HTTPS origin/i, + ); + assert.throws( + () => new BeatAPIClient({ apiKey: "test", baseUrl: "https://example.com" }), + /explicit.*operator setting/i, + ); + assert.equal( + new BeatAPIClient({ + apiKey: "test", + baseUrl: "https://example.com", + trustCustomBaseUrl: true, + }).baseUrl, + "https://example.com", + ); + assert.equal( + new BeatAPIClient({ + apiKey: "test", + baseUrl: "http://127.0.0.1:3000", + allowInsecureLocalhost: true, + }).baseUrl, + "http://127.0.0.1:3000", + ); +}); + test("parses success envelopes and sends bearer authentication", async () => { let authorization = ""; const client = new BeatAPIClient({ From cdcbb15e47c6ca798af2ca4c91d226d748edcfb8 Mon Sep 17 00:00:00 2001 From: KKKK Date: Tue, 8 Sep 2026 22:39:20 +0800 Subject: [PATCH 3/3] Forward explicit API origin trust from CLI --- packages/cli/src/cli.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index fc437e0..5814fac 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -210,6 +210,8 @@ function defaultCreateClient( return new BeatAPIClient({ apiKey, baseUrl: env.BEATAPI_BASE_URL, + allowInsecureLocalhost: env.BEATAPI_ALLOW_INSECURE_LOCALHOST === "1", + trustCustomBaseUrl: env.BEATAPI_TRUST_CUSTOM_BASE_URL === "1", }); }